diff --git a/services/lemmy/lemmy.service.js b/services/lemmy/lemmy.service.js new file mode 100644 index 0000000000000000000000000000000000000000..c8ff71ea21abe81ca572d9f830900a5c12489fb7 --- /dev/null +++ b/services/lemmy/lemmy.service.js @@ -0,0 +1,72 @@ +import Joi from 'joi' +import { metric } from '../text-formatters.js' +import { BaseJsonService, InvalidParameter } from '../index.js' + +const lemmyCommunitySchema = Joi.object({ + community_view: Joi.object({ + counts: Joi.object({ + subscribers: Joi.number().required(), + }).required(), + }).required(), +}).required() + +export default class Lemmy extends BaseJsonService { + static category = 'social' + + static route = { + base: 'lemmy', + pattern: ':community', + } + + static examples = [ + { + title: 'Lemmy', + namedParams: { community: 'asklemmy@lemmy.ml' }, + staticPreview: this.render({ + community: 'asklemmy@lemmy.ml', + members: 42, + }), + }, + ] + + static defaultBadgeData = { label: 'community' } + + static render({ community, members }) { + return { + label: `subscribe to ${community}`, + message: metric(members), + color: 'brightgreen', + } + } + + async fetch({ community }) { + const splitAlias = community.split('@') + // The community will be in the format of `community@server` + if (splitAlias.length !== 2) { + throw new InvalidParameter({ + prettyMessage: 'invalid community', + }) + } + + const host = splitAlias[1] + + const data = await this._requestJson({ + url: `https://${host}/api/v3/community`, + schema: lemmyCommunitySchema, + options: { + searchParams: { + name: community, + }, + }, + httpErrors: { + 404: 'community not found', + }, + }) + return data.community_view.counts.subscribers + } + + async handle({ community }) { + const members = await this.fetch({ community }) + return this.constructor.render({ community, members }) + } +} diff --git a/services/lemmy/lemmy.tester.js b/services/lemmy/lemmy.tester.js new file mode 100644 index 0000000000000000000000000000000000000000..b4b3ad2bfdc2d771630231bb318dcf82e5e7af00 --- /dev/null +++ b/services/lemmy/lemmy.tester.js @@ -0,0 +1,69 @@ +import Joi from 'joi' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('get community subscribers') + .get('/community@DUMMY.dumb.json') + .intercept(nock => + nock('https://DUMMY.dumb/') + .get('/api/v3/community?name=community%40DUMMY.dumb') + .reply( + 200, + JSON.stringify({ + community_view: { + counts: { + subscribers: 42, + posts: 0, + comments: 0, + }, + }, + }), + ), + ) + .expectBadge({ + label: 'subscribe to community@DUMMY.dumb', + message: '42', + color: 'brightgreen', + }) + +t.create('bad server or connection') + .get('/community@DUMMY.dumb.json') + .networkOff() + .expectBadge({ + label: 'community', + message: 'inaccessible', + color: 'lightgrey', + }) + +t.create('unknown community') + .get('/community@DUMMY.dumb.json') + .intercept(nock => + nock('https://DUMMY.dumb/') + .get('/api/v3/community?name=community%40DUMMY.dumb') + .reply( + 404, + JSON.stringify({ + error: 'couldnt_find_community', + }), + ), + ) + .expectBadge({ + label: 'community', + message: 'community not found', + color: 'red', + }) + +t.create('invalid community').get('/ALIASDUMMY.dumb.json').expectBadge({ + label: 'community', + message: 'invalid community', + color: 'red', +}) + +t.create('test on real lemmy room for API compliance') + .get('/asklemmy@lemmy.ml.json') + .timeout(10000) + .expectBadge({ + label: 'subscribe to asklemmy@lemmy.ml', + message: Joi.string().regex(/^[0-9]+k$/), + color: 'brightgreen', + })