Removing some builtin plugs

pull/3/head
Zef Hemel 2022-06-28 14:48:45 +02:00
parent 3cb8535124
commit 6cee3b82bb
10 changed files with 0 additions and 657 deletions

48
package-lock.json generated
View File

@ -1274,36 +1274,6 @@
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.0.tgz",
"integrity": "sha512-ohydQe+Hb+w4oMDvXzs8uuJd2NoA3D8YDcLiuDsLqH+yflDTPEpgCsWI3/6rH5C3BAedtH1/R51dxENldQceEA=="
},
"node_modules/@mattermost/client": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@mattermost/client/-/client-7.0.0.tgz",
"integrity": "sha512-ySzXez63Z2nzFhn2yBIe/FhWk/cWi75Xf0QXW51yQUurME/yhkilVY2yuAcbpVmOq0YhQM6WJOIKL4LKNI/Yyw==",
"dependencies": {
"form-data": "^4.0.0"
},
"peerDependencies": {
"@mattermost/types": "*",
"typescript": "^4.3"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@mattermost/types": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@mattermost/types/-/types-7.0.0.tgz",
"integrity": "sha512-8aqU1lYzZqZinZoQmxhmFYM4me5+WdVE4GWUcvwx+6FlBxd9O/VQ6YeyCFZeYVnzrcOai9MVaZdyWp29S8GBCw==",
"peerDependencies": {
"typescript": "^4.3"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@mischnic/json-sourcemap": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz",
@ -19156,8 +19126,6 @@
"@jest/globals": "^27.5.1",
"@lezer/generator": "1.0.0",
"@lezer/lr": "1.0.0",
"@mattermost/client": "^7.0.0",
"@mattermost/types": "^7.0.0",
"@silverbulletmd/common": "^0.0.8",
"@silverbulletmd/plugs": "^0.0.8",
"@silverbulletmd/web": "^0.0.8",
@ -22435,20 +22403,6 @@
}
}
},
"@mattermost/client": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@mattermost/client/-/client-7.0.0.tgz",
"integrity": "sha512-ySzXez63Z2nzFhn2yBIe/FhWk/cWi75Xf0QXW51yQUurME/yhkilVY2yuAcbpVmOq0YhQM6WJOIKL4LKNI/Yyw==",
"requires": {
"form-data": "^4.0.0"
}
},
"@mattermost/types": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@mattermost/types/-/types-7.0.0.tgz",
"integrity": "sha512-8aqU1lYzZqZinZoQmxhmFYM4me5+WdVE4GWUcvwx+6FlBxd9O/VQ6YeyCFZeYVnzrcOai9MVaZdyWp29S8GBCw==",
"requires": {}
},
"@mischnic/json-sourcemap": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz",
@ -27708,8 +27662,6 @@
"@jest/globals": "^27.5.1",
"@lezer/generator": "1.0.0",
"@lezer/lr": "1.0.0",
"@mattermost/client": "^7.0.0",
"@mattermost/types": "^7.0.0",
"@silverbulletmd/common": "^0.0.8",
"@silverbulletmd/plugs": "^0.0.8",
"@silverbulletmd/web": "^0.0.8",

View File

@ -1,9 +0,0 @@
name: ghost
functions:
publishCommand:
path: "./ghost.ts:publishCommand"
command:
name: "Ghost: Publish"
publish:
path: "./ghost.ts:publish"
env: server

View File

@ -1,220 +0,0 @@
import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space";
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system";
import {
getCurrentPage,
getText,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor";
import { cleanMarkdown } from "../markdown/util";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
import { extractMeta } from "../query/data";
type GhostConfig = {
url: string;
adminKey: string;
postPrefix: string;
pagePrefix: string;
};
type Post = {
id: string;
uuid: string;
title: string;
slug: string;
mobiledoc: string;
status: "draft" | "published";
visibility: string;
created_at: string;
upblished_at: string;
updated_at: string;
tags: Tag[];
primary_tag: Tag;
url: string;
excerpt: string;
};
type Tag = {
id: string;
name: string;
slug: string;
description: string | null;
};
type MobileDoc = {
version: string;
atoms: any[];
cards: Card[];
};
type Card = any[];
function mobileDocToMarkdown(doc: string): string | null {
let mobileDoc = JSON.parse(doc) as MobileDoc;
if (mobileDoc.cards.length > 0 && mobileDoc.cards[0][0] === "markdown") {
return mobileDoc.cards[0][1].markdown;
}
return null;
}
function markdownToMobileDoc(text: string): string {
return JSON.stringify({
version: "0.3.1",
atoms: [],
cards: [["markdown", { markdown: text }]],
markups: [],
sections: [
[10, 0],
[1, "p", []],
],
});
}
class GhostAdmin {
private token?: string;
constructor(private url: string, private key: string) {}
async init() {
const [id, secret] = this.key.split(":");
this.token = await self.syscall(
"jwt.jwt",
secret,
id,
"HS256",
"5m",
"/v3/admin/"
);
}
async listPosts(): Promise<Post[]> {
let result = await fetch(
`${this.url}/ghost/api/v3/admin/posts?order=published_at+DESC`,
{
headers: {
Authorization: `Ghost ${this.token}`,
},
}
);
return (await result.json()).posts;
}
async listMarkdownPosts(): Promise<Post[]> {
let markdownPosts: Post[] = [];
for (let post of await this.listPosts()) {
let mobileDoc = JSON.parse(post.mobiledoc) as MobileDoc;
if (mobileDoc.cards.length > 0 && mobileDoc.cards[0][0] === "markdown") {
markdownPosts.push(post);
}
}
return markdownPosts;
}
publishPost(post: Partial<Post>): Promise<any> {
return this.publish("posts", post);
}
publishPage(post: Partial<Post>): Promise<any> {
return this.publish("pages", post);
}
async publish(what: "pages" | "posts", post: Partial<Post>): Promise<any> {
let oldPostQueryR = await fetch(
`${this.url}/ghost/api/v3/admin/${what}/slug/${post.slug}`,
{
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
}
);
let oldPostQuery = await oldPostQueryR.json();
if (!oldPostQuery[what]) {
// New!
if (!post.status) {
post.status = "draft";
}
let result = await fetch(`${this.url}/ghost/api/v3/admin/${what}`, {
method: "POST",
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
[what]: [post],
}),
});
return (await result.json())[what][0];
} else {
let oldPost: Post = oldPostQuery[what][0];
post.updated_at = oldPost.updated_at;
let result = await fetch(
`${this.url}/ghost/api/v3/admin/${what}/${oldPost.id}`,
{
method: "PUT",
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
[what]: [post],
}),
}
);
return (await result.json())[what][0];
}
}
}
function postToMarkdown(post: Post): string {
let text = mobileDocToMarkdown(post.mobiledoc);
return `# ${post.title}\n${text}`;
}
const postRegex = /#\s*([^\n]+)\n([^$]+)$/;
async function markdownToPost(text: string): Promise<Partial<Post>> {
let match = postRegex.exec(text);
if (match) {
let [, title, content] = match;
return {
title,
mobiledoc: markdownToMobileDoc(await cleanMarkdown(content)),
};
}
throw Error("Post should stat with a # header");
}
async function getConfig(): Promise<GhostConfig> {
let { text } = await readPage("ghost-config");
let parsedContent = await parseMarkdown(text);
let pageMeta = await extractMeta(parsedContent);
return pageMeta as GhostConfig;
}
export async function publishCommand() {
await invokeFunction(
"server",
"publish",
await getCurrentPage(),
await getText()
);
}
export async function publish(name: string, text: string) {
let config = await getConfig();
let admin = new GhostAdmin(config.url, config.adminKey);
await admin.init();
let post = await markdownToPost(text);
if (name.startsWith(config.postPrefix)) {
post.slug = name.substring(config.postPrefix.length + 1);
await admin.publishPost(post);
console.log("Done!");
} else if (name.startsWith(config.pagePrefix)) {
post.slug = name.substring(config.pagePrefix.length + 1);
await admin.publishPage(post);
console.log("Done!");
} else {
console.error("Not in either the post or page prefix");
}
}

View File

@ -1,21 +0,0 @@
name: git
requiredPermissions:
- shell
functions:
snapshotCommand:
path: "./git.ts:snapshotCommand"
env: client
command:
name: "Git: Snapshot"
syncCommand:
path: "./git.ts:syncCommand"
env: client
command:
name: "Git: Sync"
commit:
path: "./git.ts:commit"
env: server
sync:
path: "./git.ts:sync"
env: server

View File

@ -1,48 +0,0 @@
import { run } from "@plugos/plugos-syscall/shell";
import {
flashNotification,
prompt,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor";
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system";
export async function commit(message?: string) {
if (!message) {
message = "Snapshot";
}
console.log(
"Snapshotting the current space to git with commit message",
message
);
await run("git", ["add", "./*.md"]);
try {
await run("git", ["commit", "-a", "-m", message]);
} catch (e) {
// We can ignore, this happens when there's no changes to commit
}
console.log("Done!");
}
export async function snapshotCommand() {
let revName = await prompt(`Revision name:`);
if (!revName) {
revName = "Snapshot";
}
console.log("Revision name", revName);
await invokeFunction("server", "commit", revName);
}
export async function syncCommand() {
await flashNotification("Syncing with git");
await invokeFunction("server", "sync");
await flashNotification("Git sync complete!");
}
export async function sync() {
console.log("Going to sync with git");
await commit();
console.log("Then pulling from remote");
await run("git", ["pull"]);
console.log("And then pushing to remote");
await run("git", ["push"]);
console.log("Done!");
}

View File

@ -1,10 +0,0 @@
name: github
functions:
queryEvents:
path: ./github.ts:queryEvents
events:
- query:gh-events
queryIssues:
path: ./github.ts:queryPulls
events:
- query:gh-pulls

View File

@ -1,169 +0,0 @@
import { applyQuery, QueryProviderEvent, renderQuery } from "../query/engine";
import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
import { extractMeta } from "../query/data";
type GithubEvent = {
id: string;
type: string;
actor: GithubUser;
repo: GithubRepo;
created_at: string;
payload: any;
org: GithubOrg;
};
type GithubUser = {
id: number;
login: string;
display_login: string;
url: string;
};
type GithubRepo = {
id: number;
name: string;
url: string;
};
type GithubOrg = {
id: number;
login: string;
url: string;
};
type ExposedEvent = {
id: string;
type: string;
username: string;
repo: string;
};
class GithubApi {
constructor(private token?: string) {}
async apiCall(url: string, options: any = {}): Promise<any> {
let res = await fetch(url, {
...options,
headers: {
Authorization: this.token ? `token ${this.token}` : undefined,
},
});
if (res.status !== 200) {
throw new Error(await res.text());
}
return res.json();
}
async listEvents(username: string): Promise<GithubEvent[]> {
return this.apiCall(
`https://api.github.com/users/${username}/events?per_page=100`
);
}
async listPulls(
repo: string,
state: string = "all",
sort: string = "updated"
): Promise<any[]> {
return this.apiCall(
`https://api.github.com/repos/${repo}/pulls?state=${state}&sort=${sort}&direction=desc&per_page=100`
);
}
static async fromConfig(): Promise<GithubApi> {
return new GithubApi((await getConfig()).token);
}
}
type GithubConfig = {
token?: string;
};
async function getConfig(): Promise<GithubConfig> {
try {
let { text } = await readPage("github-config");
let parsedContent = await parseMarkdown(text);
let pageMeta = await extractMeta(parsedContent);
return pageMeta as GithubConfig;
} catch (e) {
console.error("No github-config page found, using default config");
return {};
}
}
function mapEvent(event: GithubEvent): any {
// console.log("Event", event);
return {
...event.payload,
id: event.id,
type: event.type,
username: event.actor.login,
repo: event.repo.name,
date: event.created_at.split("T")[0],
};
}
export async function queryEvents({
query,
}: QueryProviderEvent): Promise<any[]> {
let api = await GithubApi.fromConfig();
let usernameFilter = query.filter.find((f) => f.prop === "username");
if (!usernameFilter) {
throw Error("No 'username' filter specified, this is mandatory");
}
let usernames: string[] = [];
if (usernameFilter.op === "=") {
usernames = [usernameFilter.value];
} else if (usernameFilter.op === "in") {
usernames = usernameFilter.value;
} else {
throw new Error(`Unsupported operator ${usernameFilter.op}`);
}
let allEvents: GithubEvent[] = [];
for (let eventList of await Promise.all(
usernames.map((username) => api.listEvents(username))
)) {
allEvents.push(...eventList);
}
// console.log("Usernames", usernames, "Event list lenght", allEvents[0]);
return applyQuery(query, allEvents.map(mapEvent));
}
function mapPull(pull: any): any {
// console.log("Pull", Object.keys(pull));
return {
...pull,
username: pull.user.login,
// repo: pull.repo.name,
createdAt: pull.created_at.split("T")[0],
updatedAt: pull.updated_at.split("T")[0],
};
}
export async function queryPulls({
query,
}: QueryProviderEvent): Promise<any[]> {
let api = await GithubApi.fromConfig();
let repo = query.filter.find((f) => f.prop === "repo");
if (!repo) {
throw Error("No 'repo' specified, this is mandatory");
}
query.filter.splice(query.filter.indexOf(repo), 1);
let repos: string[] = [];
if (repo.op === "=") {
repos = [repo.value];
} else if (repo.op === "in") {
repos = repo.value;
} else {
throw new Error(`Unsupported operator ${repo.op}`);
}
let allPulls: any[] = [];
for (let pullList of await Promise.all(
repos.map((repo) => api.listPulls(repo, "all", "updated"))
)) {
allPulls.push(...pullList);
}
allPulls = applyQuery(query, allPulls.map(mapPull));
return allPulls;
}

View File

@ -1,6 +0,0 @@
name: mattermost
functions:
test:
path: mattermost.ts:savedPostsQueryProvider
events:
- query:mm-saved

View File

@ -1,124 +0,0 @@
import { Client4 } from "@mattermost/client";
import { applyQuery, QueryProviderEvent } from "../query/engine";
import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space";
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
import { extractMeta } from "../query/data";
import { niceDate } from "../core/dates";
import { Post } from "@mattermost/types/lib/posts";
import { ServerChannel } from "@mattermost/types/lib/channels";
import { UserProfile } from "@mattermost/types/lib/users";
import { Team } from "@mattermost/types/lib/teams";
type AugmentedPost = Post & {
// Dates we can use to filter
createdAt: string;
updatedAt: string;
editedAt: string;
};
// https://community.mattermost.com/private-core/pl/rbp7a7jtr3f89nzsefo6ftqt3o
function mattermostDesktopUrlForPost(
url: string,
teamName: string,
postId: string
) {
return `${url.replace("https://", "mattermost://")}/${teamName}/pl/${postId}`;
}
type MattermostConfig = {
url: string;
token: string;
defaultTeam: string;
};
async function getConfig(): Promise<MattermostConfig> {
let { text } = await readPage("mattermost-config");
let parsedContent = await parseMarkdown(text);
let pageMeta = await extractMeta(parsedContent);
return pageMeta as MattermostConfig;
}
function augmentPost(post: AugmentedPost) {
if (post.create_at) {
post.createdAt = niceDate(new Date(post.create_at));
}
if (post.update_at) {
post.updatedAt = niceDate(new Date(post.update_at));
}
if (post.edit_at) {
post.editedAt = niceDate(new Date(post.edit_at));
}
}
class CachingClient4 {
constructor(public client: Client4) {}
private channelCache = new Map<string, ServerChannel>();
async getChannelCached(channelId: string): Promise<ServerChannel> {
let channel = this.channelCache.get(channelId);
if (channel) {
return channel;
}
channel = await this.client.getChannel(channelId);
this.channelCache.set(channelId, channel!);
return channel!;
}
private teamCache = new Map<string, Team>();
async getTeamCached(teamId: string): Promise<Team> {
let team = this.teamCache.get(teamId);
if (team) {
return team;
}
team = await this.client.getTeam(teamId);
this.teamCache.set(teamId, team!);
return team!;
}
private userCache = new Map<string, UserProfile>();
async getUserCached(userId: string): Promise<UserProfile> {
let user = this.userCache.get(userId);
if (user) {
return user;
}
user = await this.client.getUser(userId);
this.userCache.set(userId, user!);
return user!;
}
}
export async function savedPostsQueryProvider({
query,
}: QueryProviderEvent): Promise<any[]> {
let config = await getConfig();
let client = new Client4();
let cachingClient = new CachingClient4(client);
client.setUrl(config.url);
client.setToken(config.token);
let me = await client.getMe();
let postCollection = await client.getFlaggedPosts(me.id);
let savedPosts: AugmentedPost[] = [];
for (let order of postCollection.order) {
let post = postCollection.posts[order];
augmentPost(post);
savedPosts.push(post);
}
let resultSavedPosts = [];
savedPosts = applyQuery(query, savedPosts);
for (let savedPost of savedPosts) {
let channel = await cachingClient.getChannelCached(savedPost.channel_id);
let teamName = config.defaultTeam;
if (channel.team_id) {
let team = await cachingClient.getTeamCached(channel.team_id);
teamName = team.name;
}
resultSavedPosts.push({
...savedPost,
user: await cachingClient.getUserCached(savedPost.user_id),
channel: channel,
teamName: teamName,
url: mattermostDesktopUrlForPost(client.url, teamName, savedPost.id),
});
}
return resultSavedPosts;
}

View File

@ -34,8 +34,6 @@
"@jest/globals": "^27.5.1",
"@lezer/generator": "1.0.0",
"@lezer/lr": "1.0.0",
"@mattermost/client": "^7.0.0",
"@mattermost/types": "^7.0.0",
"@silverbulletmd/common": "^0.0.8",
"@silverbulletmd/plugs": "^0.0.8",
"@silverbulletmd/web": "^0.0.8",