import Router from "@koa/router"; import { koaBody } from 'koa-body'; import megalodon, { Entity, MegalodonInterface } from '@cutls/megalodon'; import { getClient } from '../ApiMastodonCompatibleService.js' import { statusModel } from './status.js'; import Autolinker from 'autolinker'; import { ParsedUrlQuery } from "querystring"; export function toLimitToInt(q: ParsedUrlQuery) { if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10).toString() return q } export function toTextWithReaction(status: Entity.Status[], host: string) { return status.map((t) => { if (!t) return statusModel(null, null, [], 'no content') if (!t.emoji_reactions) return t if (t.reblog) t.reblog = toTextWithReaction([t.reblog], host)[0] const reactions = t.emoji_reactions.map((r) => `${r.name.replace('@.', '')} (${r.count}${r.me ? "* " : ''})`); //t.emojis = getEmoji(t.content, host) t.content = `

${autoLinker(t.content, host)}

${reactions.join(', ')}

` return t }) } export function autoLinker(input: string, host: string) { return Autolinker.link(input, { hashtag: 'twitter', mention: 'twitter', email: false, stripPrefix: false, replaceFn : function (match) { switch(match.type) { case 'url': return true case 'mention': console.log("Mention: ", match.getMention()); console.log("Mention Service Name: ", match.getServiceName()); return `@${match.getMention()}`; case 'hashtag': console.log("Hashtag: ", match.getHashtag()); return `#${match.getHashtag()}`; } return false } } ); } export function apiTimelineMastodon(router: Router): void { router.get('/v1/timelines/public', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const query: any = ctx.query const data = query.local ? await client.getLocalTimeline(toLimitToInt(query)) : await client.getPublicTimeline(toLimitToInt(query)); ctx.body = toTextWithReaction(data.data, ctx.hostname); } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.get<{ Params: { hashtag: string } }>('/v1/timelines/tag/:hashtag', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.getTagTimeline(ctx.params.hashtag, toLimitToInt(ctx.query)); ctx.body = toTextWithReaction(data.data, ctx.hostname); } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.get<{ Params: { hashtag: string } }>('/v1/timelines/home', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.getHomeTimeline(toLimitToInt(ctx.query)); ctx.body = toTextWithReaction(data.data, ctx.hostname); } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.get<{ Params: { listId: string } }>('/v1/timelines/list/:listId', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.getListTimeline(ctx.params.listId, toLimitToInt(ctx.query)); ctx.body = toTextWithReaction(data.data, ctx.hostname); } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.get('/v1/conversations', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.getConversationTimeline(toLimitToInt(ctx.query)); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.get('/v1/lists', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.getLists(); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.get<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.getList(ctx.params.id); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.post('/v1/lists', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.createList((ctx.query as any).title); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.put<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.updateList(ctx.params.id, ctx.query as any); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.delete<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.deleteList(ctx.params.id); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.get<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.getAccountsInList(ctx.params.id, ctx.query as any); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.post<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.addAccountsToList(ctx.params.id, (ctx.query as any).account_ids); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); router.delete<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const data = await client.deleteAccountsFromList(ctx.params.id, (ctx.query as any).account_ids); ctx.body = data.data; } catch (e: any) { console.error(e) console.error(e.response.data) ctx.status = (401); ctx.body = e.response.data; } }); } function escapeHTML(str: string) { if (!str) { return '' } return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''') } function nl2br(str: string) { if (!str) { return '' } str = str.replace(/\r\n/g, '
') str = str.replace(/(\n|\r)/g, '
') return str }