Skip to content

Commit

Permalink
feat: add prototype guild event syncing
Browse files Browse the repository at this point in the history
  • Loading branch information
ghostdevv committed Feb 20, 2025
1 parent 1cc5616 commit 0766f08
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 1 deletion.
79 changes: 79 additions & 0 deletions pocketbase/migrations/1740067530_created_guildEventSync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package migrations

import (
"encoding/json"

"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models"
)

func init() {
m.Register(func(db dbx.Builder) error {
jsonData := `{
"id": "qnx3lm934i0qiu9",
"created": "2025-02-20 16:05:30.595Z",
"updated": "2025-02-20 16:05:30.595Z",
"name": "guildEventSync",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "9gnhopdz",
"name": "event_slug",
"type": "text",
"required": true,
"presentable": false,
"unique": false,
"options": {
"min": 1,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "lt2o1zuz",
"name": "discord_event_id",
"type": "text",
"required": true,
"presentable": false,
"unique": false,
"options": {
"min": 1,
"max": null,
"pattern": ""
}
}
],
"indexes": [
"CREATE UNIQUE INDEX ` + "`" + `idx_oH0YuDm` + "`" + ` ON ` + "`" + `guildEventSync` + "`" + ` (` + "`" + `event_slug` + "`" + `)",
"CREATE UNIQUE INDEX ` + "`" + `idx_SliJ9g4` + "`" + ` ON ` + "`" + `guildEventSync` + "`" + ` (` + "`" + `discord_event_id` + "`" + `)"
],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
}`

collection := &models.Collection{}
if err := json.Unmarshal([]byte(jsonData), &collection); err != nil {
return err
}

return daos.New(db).SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);

collection, err := dao.FindCollectionByNameOrId("qnx3lm934i0qiu9")
if err != nil {
return err
}

return dao.DeleteCollection(collection)
})
}
8 changes: 8 additions & 0 deletions src/db/pocketbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@ interface AnalyticsCollection extends BaseModel {
presence_count: number;
}

interface GuildEventSyncCollection extends BaseModel {
event_slug: string;
discord_event_id: string;
}

type LeaderboardView = Pick<ThreadSolvesCollection, 'id' | 'user_id' | 'count'>;

interface TypedPocketbase extends Pocketbase {
collection(idOrName: 'tags'): RecordService<TagsCollection>;
collection(idOrName: 'threadSolves'): RecordService<ThreadSolvesCollection>;
collection(idOrName: 'leaderboard'): RecordService<LeaderboardView>;
collection(idOrName: 'analytics'): RecordService<AnalyticsCollection>;
collection(
idOrName: 'guildEventSync',
): RecordService<GuildEventSyncCollection>;
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dotenv/config';
import { guildEventsTask } from './scheduled/guild-events';
import { analyticsTask } from './scheduled/analytics';
import { DEV_MODE, TEST_GUILD_ID } from './config';
import { Scheduler } from './scheduled/_scheduler';
Expand Down Expand Up @@ -32,7 +33,7 @@ const client = new JellyCommands({
cache: DEV_MODE,
});

new Scheduler(client).addTask(analyticsTask);
new Scheduler(client).addTask(analyticsTask).addTask(guildEventsTask);

// Auto reads the DISCORD_TOKEN environment variable
client.login();
187 changes: 187 additions & 0 deletions src/scheduled/guild-events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { DEV_MODE, TEST_GUILD_ID } from '../config';
import { ScheduledTask } from './_scheduler';
import { pb } from '../db/pocketbase';
import {
GuildScheduledEventEntityType,
GuildScheduledEventPrivacyLevel,
} from 'discord.js';
import { ClientResponseError } from 'pocketbase';

interface ResponseData {
__typename: string;
events: Events;
__isNode: string;
id: string;
}

interface Events {
edges: Edge[];
pageInfo: PageInfo;
}

interface Edge {
node: Node;
cursor: string;
}

interface Node {
id: string;
slug: string;
prettyUrl: string;
fullUrl: string;
name: string;
description: string;
startAt: string;
endAt: string;
timeZone: string;
visibility: string;
hasVenue: boolean;
hasExternalUrl: boolean;
owner: Owner;
uploadedSocialCard: any;
generatedSocialCardURL: string;
presentations: Presentations;
venue: Venue;
createdAt: string;
updatedAt: string;
}

interface Owner {
__typename: string;
id: string;
name: string;
__isNode: string;
}

interface Presentations {
edges: Edge2[];
}

interface Edge2 {
node: Node2;
cursor: string;
}

interface Node2 {
id: string;
slug: string;
prettyUrl: string;
presenter?: Presenter;
presenterFirstName?: string;
presenterLastName?: string;
title: string;
description: string;
videoSourceUrl: string;
}

interface Presenter {
id: string;
firstName: string;
lastName: string;
}

interface Venue {
address: Address;
id: string;
}

interface Address {
location: Location;
id: string;
}

interface Location {
__typename: string;
geojson: Geojson;
}

interface Geojson {
type: string;
coordinates: number[];
}

interface PageInfo {
hasPreviousPage: boolean;
hasNextPage: boolean;
startCursor: string;
endCursor: string;
}

// todo doesn't handle pagination

export const guildEventsTask: ScheduledTask = {
interval: 86400,
name: 'guild-events',
async handle(client) {
try {
console.log('Fetching guild events');

const response = await fetch(
'https://guild.host/api/next/svelte-society-london/events/upcoming',
{
headers: {
'User-Agent':
'Svelte Bot (+https://github.com/sveltejs/discord-bot)',
Accept: 'application/json',
},
},
);

const data: ResponseData = await response.json();

const guild = await client.guilds.fetch(
DEV_MODE ? TEST_GUILD_ID : '457912077277855764',
);

if (!guild) {
throw new Error('Failed to fetch guild');
}

for (const event of data.events.edges) {
const event_slug = event.node.prettyUrl;

const exists = await pb
.collection('guildEventSync')
.getFirstListItem(
pb.filter('event_slug = {:event_slug}', { event_slug }),
)
.then(() => true)
.catch((e: ClientResponseError) => {
if (e.status == 404) return false;
else throw e;
});

if (exists) {
// prettier-ignore
console.log(` Skipping ${event.node.name} as it already exists`);
continue;
}

console.log(` Creating ${event.node.name}`);

const discordEvent = await guild.scheduledEvents.create({
name: event.node.name,
image: event.node.generatedSocialCardURL.replace(
/\.svg$/,
'.png',
),
description: event.node.description,
scheduledStartTime: event.node.startAt,
scheduledEndTime: event.node.endAt,
entityType: GuildScheduledEventEntityType.External,
privacyLevel: GuildScheduledEventPrivacyLevel.GuildOnly,
entityMetadata: {
location: event.node.fullUrl,
},
});

await pb.collection('guildEventSync').create({
event_slug,
discord_event_id: discordEvent.id,
});
}
} catch (error) {
console.error('failed to save analytics', error);
}
},
};

0 comments on commit 0766f08

Please sign in to comment.