djs-bridge 0.0.0-stage → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -2
- package/distribution/bot.d.ts +5 -0
- package/distribution/bot.discord.d.ts +17 -0
- package/distribution/bot.discord.js +182 -0
- package/distribution/bot.discord.js.map +1 -0
- package/distribution/bot.fluxer.d.ts +18 -0
- package/distribution/bot.fluxer.js +183 -0
- package/distribution/bot.fluxer.js.map +1 -0
- package/distribution/bot.interface.d.ts +88 -0
- package/distribution/bot.interface.js +90 -0
- package/distribution/bot.interface.js.map +1 -0
- package/distribution/bot.js +11 -0
- package/distribution/bot.js.map +1 -0
- package/distribution/db-migrations.d.ts +3 -0
- package/distribution/db-migrations.js +20 -0
- package/distribution/db-migrations.js.map +1 -0
- package/distribution/db.d.ts +26 -0
- package/distribution/db.js +30 -0
- package/distribution/db.js.map +1 -0
- package/distribution/index.d.ts +7 -0
- package/distribution/index.js +30 -0
- package/distribution/index.js.map +1 -0
- package/distribution/main.d.ts +2 -0
- package/distribution/main.js +4 -0
- package/distribution/main.js.map +1 -0
- package/distribution/rest.d.ts +9 -0
- package/distribution/rest.js +36 -0
- package/distribution/rest.js.map +1 -0
- package/distribution/util.d.ts +1 -0
- package/distribution/util.js +7 -0
- package/distribution/util.js.map +1 -0
- package/package.json +57 -3
package/README.md
CHANGED
|
@@ -1,3 +1,77 @@
|
|
|
1
|
-
#
|
|
1
|
+
# djs-bridge
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="https://codeberg.org/sanin/djs-bridge/raw/branch/master/assets/djs-bridge.svg" alt="djs-bridge icon; a white silhouette of a bridge against a blurple background" width="200" height="200"/>
|
|
4
|
+
|
|
5
|
+
Yet another Discord ↔️ Fluxer bridge application.
|
|
6
|
+
|
|
7
|
+
## Why Another?
|
|
8
|
+
|
|
9
|
+
I started building this before I discovered other similar applications. Popular amongst which are [Bifröst](https://bifrost-bot.com/) and [Fluxcord](https://fluxcord.jbcrn.dev/). While fulfilling the same goals, djs-bridge still manages to stand out by being lean and simple. Bifröst has a web frontend which is handy for hosted services but overkill for a self-hosted bot. Fluxcord supports bridging voice chats, which adds to its complexity and isn't always needed or desired.
|
|
10
|
+
|
|
11
|
+
djs-bridge was built from the ground-up to be a basic text-chat mirroring application. One SQLite table persists message mapping data. Configuration is done with one configuration file. No bot commands. The bare minimum to get chat relayed between guilds.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Messages are automatically relayed to channels across guilds with matching names
|
|
16
|
+
- Users' usernames and avatars are displayed via webhooks
|
|
17
|
+
- Attachments are supported
|
|
18
|
+
- Custom emoji is used when present on the destination guild
|
|
19
|
+
- Message edits and deletions are propogated across guilds
|
|
20
|
+
- Reactions kind of work. The bot can't do more than 1 reaction per emoji.
|
|
21
|
+
|
|
22
|
+
## Planned
|
|
23
|
+
|
|
24
|
+
- Stickers (as attachments)
|
|
25
|
+
- Threads
|
|
26
|
+
- Mirroring bot activity
|
|
27
|
+
|
|
28
|
+
## Out of scope
|
|
29
|
+
|
|
30
|
+
These features may exists in the alternatives mentioned above. They will not come to djs-bridge.
|
|
31
|
+
|
|
32
|
+
- Polls
|
|
33
|
+
- Voice chat bridging
|
|
34
|
+
- Members online count/status
|
|
35
|
+
|
|
36
|
+
# How to
|
|
37
|
+
|
|
38
|
+
The bot is available via [Codeberg's container registry](https://codeberg.org/sanin/-/packages/container/djs-bridge/), [Docker hub](https://hub.docker.com/r/corysanin/djs-bridge), [npm](https://www.npmjs.com/package/djs-bridge), or via source.
|
|
39
|
+
|
|
40
|
+
See [docker-compose.yml](https://codeberg.org/sanin/djs-bridge/src/branch/master/docker-compose.yml) for an example Docker deployment.
|
|
41
|
+
|
|
42
|
+
## Build from source
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://codeberg.org/sanin/djs-bridge.git
|
|
46
|
+
cd djs-bridge
|
|
47
|
+
pnpm install
|
|
48
|
+
pnpm run build
|
|
49
|
+
cp config/config.example.yaml cp config/config.yaml # edit this file
|
|
50
|
+
pnpm run start
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
db:
|
|
57
|
+
# Where to store sqlite DB. Defaults to `./db/messages.db`
|
|
58
|
+
file: messages.db
|
|
59
|
+
# Whether to skip db migrations or not. Defaults to `false`
|
|
60
|
+
skipMigrations: false
|
|
61
|
+
# An array of bots to relay messages between. Should have at least two.
|
|
62
|
+
bots:
|
|
63
|
+
# The name that appears after usernames in relayed messages. Also used in the DB, must be unique. Required.
|
|
64
|
+
- name: A Fluxer Server
|
|
65
|
+
# The web address that will be used for the bot API. Defaults to `https://discord.com`
|
|
66
|
+
server: https://fluxer.com
|
|
67
|
+
# Bot token for the respective platform. Required.
|
|
68
|
+
token: 123_abc_xyz
|
|
69
|
+
# ID for the guild on this server to sync chat to/from. Required.
|
|
70
|
+
guild: '225989349949308928'
|
|
71
|
+
# ID for the channel in this guild to post messages to if a matching channel can't be found. Required.
|
|
72
|
+
defaultChannel: '1569188535674601475'
|
|
73
|
+
# True doesn't upload the incoming avatar to this server. Useful if you're concerned about disk space. Defaults to `false`.
|
|
74
|
+
suppressAvatar: false
|
|
75
|
+
- name: A Discord Server
|
|
76
|
+
...
|
|
77
|
+
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DiscordBot } from './bot.discord.js';
|
|
2
|
+
import { FluxerBot } from './bot.fluxer.js';
|
|
3
|
+
import type { BotConfig } from './bot.interface.js';
|
|
4
|
+
import type { DB } from './db.js';
|
|
5
|
+
export declare function createBot(cfg: BotConfig, db: DB): Promise<DiscordBot | FluxerBot>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Guild, GuildBasedChannel } from 'discord.js';
|
|
2
|
+
import { iBot, type MessagePayload, type BotConfig, GetChannelOptions, GetDestinationOptions, ReactPayload } from './bot.interface.js';
|
|
3
|
+
import type { DB } from './db.js';
|
|
4
|
+
export declare class DiscordBot extends iBot<Guild, GuildBasedChannel> {
|
|
5
|
+
private client;
|
|
6
|
+
constructor(cfg: BotConfig, db: DB);
|
|
7
|
+
private getClient;
|
|
8
|
+
init(): Promise<void>;
|
|
9
|
+
getGuild: () => Promise<Guild>;
|
|
10
|
+
getChannel: (options: GetChannelOptions) => Promise<GuildBasedChannel | null>;
|
|
11
|
+
findEmoji: (guild: Guild, name?: string | null) => import("discord.js").GuildEmoji | undefined;
|
|
12
|
+
resolveEmoji: (nameOrEmoji: string) => Promise<string | import("discord.js").GuildEmoji | undefined>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
deleteMessage: (payload: GetDestinationOptions & GetChannelOptions) => Promise<void>;
|
|
15
|
+
sendMessage: (payload: MessagePayload) => Promise<void>;
|
|
16
|
+
sendReact: (payload: ReactPayload) => Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Client, GatewayIntentBits, Events, ChannelType } from 'discord.js';
|
|
2
|
+
import { iBot } from './bot.interface.js';
|
|
3
|
+
export class DiscordBot extends iBot {
|
|
4
|
+
client;
|
|
5
|
+
constructor(cfg, db) {
|
|
6
|
+
super(cfg, db);
|
|
7
|
+
}
|
|
8
|
+
getClient() {
|
|
9
|
+
if (this.client) {
|
|
10
|
+
return this.client;
|
|
11
|
+
}
|
|
12
|
+
throw new Error('Bot not initialized!');
|
|
13
|
+
}
|
|
14
|
+
async init() {
|
|
15
|
+
const client = this.client = new Client({
|
|
16
|
+
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildWebhooks, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.MessageContent],
|
|
17
|
+
allowedMentions: { parse: [] }
|
|
18
|
+
});
|
|
19
|
+
await client.login(this.config.token);
|
|
20
|
+
console.log(`[${this.config.name}] logged in to Discord as ${client.user?.username}`);
|
|
21
|
+
const handleMessage = async (emsg, newmsg) => {
|
|
22
|
+
const msg = newmsg ? (newmsg.partial ? await newmsg.fetch() : newmsg) : emsg;
|
|
23
|
+
if (!msg || msg.guild?.id !== this.config.guild || msg.member?.user.bot || msg.webhookId) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (newmsg && this.isEditTooLate(emsg.createdTimestamp)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const channel = msg.channel;
|
|
30
|
+
const payload = {
|
|
31
|
+
action: newmsg ? 'update' : 'send',
|
|
32
|
+
source: this.config.name,
|
|
33
|
+
voice: channel.type === ChannelType.GuildVoice,
|
|
34
|
+
username: msg.member?.nickname ?? msg.author?.username ?? 'UNKNOWN USER',
|
|
35
|
+
channel: channel.name ?? this.config.defaultChannel,
|
|
36
|
+
content: msg.content,
|
|
37
|
+
originatingId: msg.id,
|
|
38
|
+
};
|
|
39
|
+
msg.attachments && (payload.attachments = msg.attachments.map((v, _k) => {
|
|
40
|
+
const att = {
|
|
41
|
+
description: v.description,
|
|
42
|
+
attachment: v.url,
|
|
43
|
+
name: v.name,
|
|
44
|
+
};
|
|
45
|
+
v.waveform && (att.waveform = v.waveform);
|
|
46
|
+
v.title && (att.title = v.title);
|
|
47
|
+
v.duration && (att.duration = v.duration);
|
|
48
|
+
return att;
|
|
49
|
+
}));
|
|
50
|
+
const avatarUrl = msg.member?.avatarURL() ?? msg.member?.user.avatarURL() ?? msg.author?.avatarURL();
|
|
51
|
+
avatarUrl && (payload.avatar = avatarUrl);
|
|
52
|
+
this.emit('message', payload);
|
|
53
|
+
};
|
|
54
|
+
client.on(Events.MessageUpdate, handleMessage);
|
|
55
|
+
client.on(Events.MessageCreate, handleMessage);
|
|
56
|
+
client.on(Events.MessageDelete, e => {
|
|
57
|
+
const channel = e.channel;
|
|
58
|
+
const payload = {
|
|
59
|
+
action: 'delete',
|
|
60
|
+
source: this.config.name,
|
|
61
|
+
voice: channel.type === ChannelType.GuildVoice,
|
|
62
|
+
username: 'N/A',
|
|
63
|
+
channel: channel.name ?? this.config.defaultChannel,
|
|
64
|
+
content: null,
|
|
65
|
+
originatingId: e.id,
|
|
66
|
+
};
|
|
67
|
+
this.emit('message', payload);
|
|
68
|
+
});
|
|
69
|
+
const handleReact = (reaction, add) => {
|
|
70
|
+
if ((add ? reaction.count !== 1 : reaction.count !== 0) || (!!reaction.users.cache.size && reaction.users.cache.every(user => user.bot))) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const channel = reaction.message.channel;
|
|
74
|
+
const source = this.getSource({
|
|
75
|
+
destination: this.config.name,
|
|
76
|
+
destinationid: reaction.message.id
|
|
77
|
+
});
|
|
78
|
+
const payload = {
|
|
79
|
+
action: add ? 'add' : 'remove',
|
|
80
|
+
source: source?.source ?? this.config.name,
|
|
81
|
+
voice: channel.type === ChannelType.GuildVoice,
|
|
82
|
+
channel: channel.name ?? this.config.defaultChannel,
|
|
83
|
+
originatingId: source?.sourceid ?? reaction.message.id,
|
|
84
|
+
emoji: reaction.emoji.id ? `:${reaction.emoji.name}:` : reaction.emoji.name,
|
|
85
|
+
};
|
|
86
|
+
this.emit('react', payload);
|
|
87
|
+
};
|
|
88
|
+
client.on(Events.MessageReactionAdd, (reaction, _user) => handleReact(reaction, true));
|
|
89
|
+
client.on(Events.MessageReactionRemove, (reaction, _user) => handleReact(reaction, false));
|
|
90
|
+
}
|
|
91
|
+
getGuild = async () => {
|
|
92
|
+
const client = this.getClient();
|
|
93
|
+
return await client.guilds.fetch({ guild: this.config.guild, withCounts: false });
|
|
94
|
+
};
|
|
95
|
+
getChannel = async (options) => {
|
|
96
|
+
const guild = await this.getGuild();
|
|
97
|
+
return guild.channels.cache.find(ch => ch.name === options.channel && (ch.type === ChannelType.GuildVoice) === options.voice) ??
|
|
98
|
+
await guild.channels.fetch(this.config.defaultChannel);
|
|
99
|
+
};
|
|
100
|
+
findEmoji = (guild, name) => {
|
|
101
|
+
return guild.emojis.cache.find(emoji => emoji.name === name);
|
|
102
|
+
};
|
|
103
|
+
resolveEmoji = async (nameOrEmoji) => {
|
|
104
|
+
if (!nameOrEmoji.startsWith(':')) {
|
|
105
|
+
return nameOrEmoji;
|
|
106
|
+
}
|
|
107
|
+
const guild = (await this.getGuild());
|
|
108
|
+
const identifier = guild.emojis.resolveIdentifier(nameOrEmoji);
|
|
109
|
+
return this.findEmoji(guild, identifier);
|
|
110
|
+
};
|
|
111
|
+
async close() {
|
|
112
|
+
await this.client?.destroy();
|
|
113
|
+
}
|
|
114
|
+
deleteMessage = async (payload) => {
|
|
115
|
+
const existingMessage = this.getDestination(payload, this.config.name);
|
|
116
|
+
const channel = await this.getChannel(payload);
|
|
117
|
+
if (existingMessage && channel && 'messages' in channel) {
|
|
118
|
+
try {
|
|
119
|
+
await (await channel.messages.fetch(existingMessage)).delete();
|
|
120
|
+
}
|
|
121
|
+
catch (ex) {
|
|
122
|
+
console.error('unable to delete previous message', ex);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
sendMessage = async (payload) => {
|
|
127
|
+
await this.deleteMessage(payload);
|
|
128
|
+
if (payload.action === 'delete') {
|
|
129
|
+
this.deleteDestination(payload, this.config.name);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const guild = await this.getGuild();
|
|
133
|
+
const channel = await this.getChannel(payload);
|
|
134
|
+
if (!channel || !('send' in channel) || channel.isThread()) {
|
|
135
|
+
throw new Error(`failed to find ${this.config.guild}/${payload.channel} nor default channel with ID ${this.config.defaultChannel}`);
|
|
136
|
+
}
|
|
137
|
+
const webhook = await guild.channels.createWebhook({
|
|
138
|
+
avatar: ((!this.config.suppressAvatar) && payload.avatar) || null,
|
|
139
|
+
channel: channel,
|
|
140
|
+
name: `${payload.username} (${payload.source})${payload.action === 'update' ? '*' : ''}`.replace(/discord/gi, "discorɗ"),
|
|
141
|
+
reason: 'bridge bot message'
|
|
142
|
+
});
|
|
143
|
+
const responsePayload = {
|
|
144
|
+
content: await this.insertEmoji(payload.content ?? ''),
|
|
145
|
+
};
|
|
146
|
+
payload.attachments && (responsePayload.files = payload.attachments);
|
|
147
|
+
for (let i = 0; i < 2; i++) {
|
|
148
|
+
try {
|
|
149
|
+
const msg = await webhook.send(responsePayload);
|
|
150
|
+
msg?.id && this.upsert(payload, this.config.name, msg.id);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
catch (ex) {
|
|
154
|
+
console.error(ex);
|
|
155
|
+
if (!responsePayload.files) {
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
delete responsePayload.files;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
await webhook.delete();
|
|
162
|
+
};
|
|
163
|
+
sendReact = async (payload) => {
|
|
164
|
+
const msgId = payload.source === this.config.name ? payload.originatingId : this.getDestination(payload, this.config.name);
|
|
165
|
+
const channel = await this.getChannel(payload);
|
|
166
|
+
if (!channel || !msgId || !('messages' in channel)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const msg = await channel.messages.fetch(msgId);
|
|
170
|
+
const emoji = await this.resolveEmoji(payload.emoji);
|
|
171
|
+
if (!emoji) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (payload.action === 'add') {
|
|
175
|
+
await msg.react(emoji);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
msg.reactions.cache.find(r => r.me)?.remove();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=bot.discord.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.discord.js","sourceRoot":"","sources":["../src/bot.discord.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Guild, GuildChannel } from '@fluxerjs/core';
|
|
2
|
+
import { iBot } from './bot.interface.js';
|
|
3
|
+
import type { MessagePayload, BotConfig, GetDestinationOptions, GetChannelOptions, ReactPayload } from './bot.interface.js';
|
|
4
|
+
import type { DB } from './db.js';
|
|
5
|
+
export declare class FluxerBot extends iBot<Guild, GuildChannel> {
|
|
6
|
+
private client;
|
|
7
|
+
constructor(cfg: BotConfig, db: DB);
|
|
8
|
+
private getClient;
|
|
9
|
+
init(): Promise<void>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
getGuild: () => Promise<Guild>;
|
|
12
|
+
getChannel: (options: GetChannelOptions) => Promise<GuildChannel | null>;
|
|
13
|
+
findEmoji: (guild: Guild, name?: string | null) => import("@fluxerjs/core").GuildEmoji | undefined;
|
|
14
|
+
resolveEmoji: (nameOrEmoji: string) => Promise<string | import("@fluxerjs/core").GuildEmoji | undefined>;
|
|
15
|
+
deleteMessage: (payload: GetDestinationOptions & GetChannelOptions) => Promise<void>;
|
|
16
|
+
sendMessage: (payload: MessagePayload) => Promise<void>;
|
|
17
|
+
sendReact: (payload: ReactPayload) => Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { Client, Events, ChannelType } from '@fluxerjs/core';
|
|
2
|
+
import { iBot } from './bot.interface.js';
|
|
3
|
+
export class FluxerBot extends iBot {
|
|
4
|
+
client;
|
|
5
|
+
constructor(cfg, db) {
|
|
6
|
+
super(cfg, db);
|
|
7
|
+
}
|
|
8
|
+
getClient() {
|
|
9
|
+
if (this.client) {
|
|
10
|
+
return this.client;
|
|
11
|
+
}
|
|
12
|
+
throw new Error('Bot not initialized!');
|
|
13
|
+
}
|
|
14
|
+
async init() {
|
|
15
|
+
const client = this.client = await Client.fromDiscovery(this.config.server);
|
|
16
|
+
await client.login(this.config.token);
|
|
17
|
+
console.log(`[${this.config.name}] logged in to ${this.config.server}`);
|
|
18
|
+
const handleMessage = async (emsg, newmsg) => {
|
|
19
|
+
const msg = newmsg ? (newmsg.partial ? await newmsg.fetch() : newmsg) : emsg;
|
|
20
|
+
if (!msg || msg.guildId !== this.config.guild || msg.author?.bot || ('webhookId' in msg && msg.webhookId)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (newmsg && emsg?.createdAt && this.isEditTooLate(emsg.createdAt)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const channel = msg.channel;
|
|
27
|
+
const payload = {
|
|
28
|
+
action: newmsg ? 'update' : 'send',
|
|
29
|
+
source: this.config.name,
|
|
30
|
+
voice: channel.type === ChannelType.GuildVoice,
|
|
31
|
+
username: msg.member?.nick ?? msg.author?.username ?? 'UNKNOWN USER',
|
|
32
|
+
channel: channel.name ?? this.config.defaultChannel,
|
|
33
|
+
content: msg?.content,
|
|
34
|
+
originatingId: msg.id
|
|
35
|
+
};
|
|
36
|
+
msg.attachments && (payload.attachments = msg.attachments.map((v, _k) => {
|
|
37
|
+
const att = {
|
|
38
|
+
description: v.description,
|
|
39
|
+
attachment: v.url,
|
|
40
|
+
name: v.filename,
|
|
41
|
+
};
|
|
42
|
+
v.waveform && (att.waveform = v.waveform);
|
|
43
|
+
v.title && (att.title = v.title);
|
|
44
|
+
v.duration && (att.duration = v.duration);
|
|
45
|
+
return att;
|
|
46
|
+
}));
|
|
47
|
+
const avatarUrl = msg.member?.avatarURL() ?? msg.member?.user.avatarURL() ?? msg.author.avatarURL();
|
|
48
|
+
avatarUrl && (payload.avatar = avatarUrl);
|
|
49
|
+
this.emit('message', payload);
|
|
50
|
+
};
|
|
51
|
+
client.on(Events.MessageCreate, handleMessage);
|
|
52
|
+
client.on(Events.MessageUpdate, handleMessage);
|
|
53
|
+
client.on(Events.MessageDelete, e => {
|
|
54
|
+
const channel = e.channel;
|
|
55
|
+
const payload = {
|
|
56
|
+
action: 'delete',
|
|
57
|
+
source: this.config.name,
|
|
58
|
+
voice: channel.type === ChannelType.GuildVoice,
|
|
59
|
+
username: 'N/A',
|
|
60
|
+
channel: channel.name ?? this.config.defaultChannel,
|
|
61
|
+
content: null,
|
|
62
|
+
originatingId: e.id
|
|
63
|
+
};
|
|
64
|
+
this.emit('message', payload);
|
|
65
|
+
});
|
|
66
|
+
const handleReact = (reaction, add) => {
|
|
67
|
+
// as of 2026-11-10 reaction.reaction.count is returned before being decremented
|
|
68
|
+
if ((add ? reaction.reaction.count !== 1 : reaction.reaction.count > 1) || reaction.user.bot) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const channel = reaction.channel;
|
|
72
|
+
const source = this.getSource({
|
|
73
|
+
destination: this.config.name,
|
|
74
|
+
destinationid: reaction.messageId
|
|
75
|
+
});
|
|
76
|
+
const payload = {
|
|
77
|
+
action: add ? 'add' : 'remove',
|
|
78
|
+
source: source?.source ?? this.config.name,
|
|
79
|
+
voice: channel.type === ChannelType.GuildVoice,
|
|
80
|
+
channel: channel.name ?? this.config.defaultChannel,
|
|
81
|
+
originatingId: source?.sourceid ?? reaction.messageId,
|
|
82
|
+
emoji: reaction.emoji.id ? `:${reaction.emoji.name}:` : reaction.emoji.name,
|
|
83
|
+
};
|
|
84
|
+
this.emit('react', payload);
|
|
85
|
+
};
|
|
86
|
+
client.on(Events.MessageReactionAdd, (react) => handleReact(react, true));
|
|
87
|
+
client.on(Events.MessageReactionRemove, (react) => handleReact(react, false));
|
|
88
|
+
}
|
|
89
|
+
async close() {
|
|
90
|
+
await this.client?.destroy();
|
|
91
|
+
}
|
|
92
|
+
getGuild = async () => {
|
|
93
|
+
const client = this.getClient();
|
|
94
|
+
return await client.guilds.fetch(this.config.guild);
|
|
95
|
+
};
|
|
96
|
+
getChannel = async (options) => {
|
|
97
|
+
const guild = await this.getGuild();
|
|
98
|
+
return guild.channels.find(ch => ch.name === options.channel && (ch.type === ChannelType.GuildVoice) === options.voice) ??
|
|
99
|
+
guild.channels.get(this.config.defaultChannel) ?? null;
|
|
100
|
+
};
|
|
101
|
+
findEmoji = (guild, name) => {
|
|
102
|
+
return guild.emojis.find(emoji => emoji.name === name);
|
|
103
|
+
};
|
|
104
|
+
resolveEmoji = async (nameOrEmoji) => {
|
|
105
|
+
if (!nameOrEmoji.startsWith(':')) {
|
|
106
|
+
return nameOrEmoji;
|
|
107
|
+
}
|
|
108
|
+
const identifier = nameOrEmoji.substring(1, nameOrEmoji.length - 1);
|
|
109
|
+
return this.findEmoji(await this.getGuild(), identifier);
|
|
110
|
+
};
|
|
111
|
+
deleteMessage = async (payload) => {
|
|
112
|
+
const existingMessage = this.getDestination(payload, this.config.name);
|
|
113
|
+
const channel = await this.getChannel(payload);
|
|
114
|
+
if (existingMessage && channel) {
|
|
115
|
+
try {
|
|
116
|
+
await (await channel.messages.fetch(existingMessage)).delete();
|
|
117
|
+
}
|
|
118
|
+
catch (ex) {
|
|
119
|
+
console.error('unable to delete previous message', ex);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
sendMessage = async (payload) => {
|
|
124
|
+
await this.deleteMessage(payload);
|
|
125
|
+
if (payload.action === 'delete') {
|
|
126
|
+
this.deleteDestination(payload, this.config.name);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const channel = await this.getChannel(payload);
|
|
130
|
+
if (!channel || !('send' in channel) || channel.isThread()) {
|
|
131
|
+
throw new Error(`failed to find ${this.config.guild}/${payload.channel} nor default channel with ID ${this.config.defaultChannel}`);
|
|
132
|
+
}
|
|
133
|
+
const webhook = await channel.createWebhook({
|
|
134
|
+
name: `${payload.username} (${payload.source})${payload.action === 'update' ? '*' : ''}`
|
|
135
|
+
});
|
|
136
|
+
const avatarUrl = (!this.config.suppressAvatar) && payload.avatar;
|
|
137
|
+
const responsePayload = {
|
|
138
|
+
content: await this.insertEmoji(payload.content ?? ''),
|
|
139
|
+
};
|
|
140
|
+
avatarUrl && (responsePayload.avatarUrl = avatarUrl);
|
|
141
|
+
payload.attachments && (responsePayload.files = payload.attachments.map(v => {
|
|
142
|
+
return {
|
|
143
|
+
name: v.name,
|
|
144
|
+
url: v.attachment,
|
|
145
|
+
filename: v.title
|
|
146
|
+
};
|
|
147
|
+
}));
|
|
148
|
+
for (let i = 0; i < 2; i++) {
|
|
149
|
+
try {
|
|
150
|
+
const msg = await webhook.send(responsePayload, true);
|
|
151
|
+
msg?.id && this.upsert(payload, this.config.name, msg.id);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
catch (ex) {
|
|
155
|
+
console.error(ex);
|
|
156
|
+
if (!responsePayload.files) {
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
delete responsePayload.files;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
await webhook.delete();
|
|
163
|
+
};
|
|
164
|
+
sendReact = async (payload) => {
|
|
165
|
+
const msgId = payload.source === this.config.name ? payload.originatingId : this.getDestination(payload, this.config.name);
|
|
166
|
+
const channel = await this.getChannel(payload);
|
|
167
|
+
if (!channel || !msgId || !('messages' in channel)) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const msg = await channel.messages.fetch(msgId);
|
|
171
|
+
const emoji = await this.resolveEmoji(payload.emoji);
|
|
172
|
+
if (!emoji) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (payload.action === 'add') {
|
|
176
|
+
await msg.react(emoji);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
this.client?.user?.id && msg.removeReaction(emoji, this.client.user.id);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=bot.fluxer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.fluxer.js","sourceRoot":"","sources":["../src/bot.fluxer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import type { AttachmentPayload } from 'discord.js';
|
|
3
|
+
import type { DB, ReverseKey } from './db.js';
|
|
4
|
+
export interface BotConfig {
|
|
5
|
+
/**
|
|
6
|
+
* max age for a message before edits stop being propogated (ms).
|
|
7
|
+
* 0 disables propogating edits entirely, -1 removes the expiration period.
|
|
8
|
+
*/
|
|
9
|
+
editMaxAge?: number;
|
|
10
|
+
/**
|
|
11
|
+
* If true, do not relay the sender's avatar
|
|
12
|
+
*/
|
|
13
|
+
suppressAvatar?: boolean;
|
|
14
|
+
server?: string;
|
|
15
|
+
name: string;
|
|
16
|
+
token: string;
|
|
17
|
+
guild: string;
|
|
18
|
+
/**
|
|
19
|
+
* snowflake for fallback text channel
|
|
20
|
+
*/
|
|
21
|
+
defaultChannel: string;
|
|
22
|
+
}
|
|
23
|
+
export type SendPayloadAction = 'send' | 'update' | 'delete';
|
|
24
|
+
export interface MessagePayload {
|
|
25
|
+
action: SendPayloadAction;
|
|
26
|
+
source: string;
|
|
27
|
+
voice: boolean;
|
|
28
|
+
channel: string;
|
|
29
|
+
username: string;
|
|
30
|
+
content: string | null;
|
|
31
|
+
originatingId: string;
|
|
32
|
+
avatar?: string;
|
|
33
|
+
attachments?: AttachmentPayload[];
|
|
34
|
+
}
|
|
35
|
+
export type EmojiPayloadAction = 'add' | 'remove';
|
|
36
|
+
export interface ReactPayload {
|
|
37
|
+
action: EmojiPayloadAction;
|
|
38
|
+
source: string;
|
|
39
|
+
voice: boolean;
|
|
40
|
+
channel: string;
|
|
41
|
+
originatingId: string;
|
|
42
|
+
emoji: `:${string}:` | string;
|
|
43
|
+
}
|
|
44
|
+
export interface GetDestinationOptions {
|
|
45
|
+
source: string;
|
|
46
|
+
originatingId: string;
|
|
47
|
+
}
|
|
48
|
+
export interface GetChannelOptions {
|
|
49
|
+
channel: string;
|
|
50
|
+
voice: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface EmojiReplacements {
|
|
53
|
+
match: string;
|
|
54
|
+
replacement?: string;
|
|
55
|
+
}
|
|
56
|
+
interface BotEvents {
|
|
57
|
+
message: (message: MessagePayload) => void;
|
|
58
|
+
react: (message: ReactPayload) => void;
|
|
59
|
+
}
|
|
60
|
+
export declare interface iBot<T, U> {
|
|
61
|
+
on<T extends keyof BotEvents>(event: T, listener: BotEvents[T]): this;
|
|
62
|
+
emit<T extends keyof BotEvents>(event: T, ...args: Parameters<BotEvents[T]>): boolean;
|
|
63
|
+
}
|
|
64
|
+
export declare abstract class iBot<T, U> extends EventEmitter {
|
|
65
|
+
protected config: BotConfig;
|
|
66
|
+
protected db: DB;
|
|
67
|
+
constructor(cfg: BotConfig, db: DB);
|
|
68
|
+
abstract init(): Promise<void>;
|
|
69
|
+
abstract getGuild(): Promise<T>;
|
|
70
|
+
abstract getChannel(options: GetChannelOptions): Promise<U | null>;
|
|
71
|
+
abstract deleteMessage(payload: GetDestinationOptions & GetChannelOptions): Promise<void>;
|
|
72
|
+
abstract sendMessage(payload: MessagePayload): Promise<void>;
|
|
73
|
+
abstract sendReact(payload: ReactPayload): Promise<void>;
|
|
74
|
+
abstract findEmoji(guild: T, name?: string | null): {
|
|
75
|
+
name: string;
|
|
76
|
+
id: string;
|
|
77
|
+
animated: boolean;
|
|
78
|
+
} | undefined;
|
|
79
|
+
abstract close(): Promise<void>;
|
|
80
|
+
isEditTooLate: (timestamp: number | Date) => boolean;
|
|
81
|
+
createListeners(bots: iBot<any, any>[]): void;
|
|
82
|
+
insertEmoji: (message: string) => Promise<string>;
|
|
83
|
+
protected upsert: (payload: MessagePayload, destination: string, destinationid: string) => void;
|
|
84
|
+
protected getDestination: (payload: MessagePayload | GetDestinationOptions, destination: string) => string | undefined;
|
|
85
|
+
protected getSource: (query: ReverseKey) => import("./db.js").Message | undefined;
|
|
86
|
+
protected deleteDestination: (payload: MessagePayload | GetDestinationOptions, destination: string) => void;
|
|
87
|
+
}
|
|
88
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
import { notStupidParseInt } from './util.js';
|
|
4
|
+
const EMOJI_CHARS = /[a-zA-Z0-9:_]/;
|
|
5
|
+
const DEFAULT_MAX_EDIT_AGE = notStupidParseInt(process.env['EDIT_MAX_AGE'] ?? 1000 * 60 * 2);
|
|
6
|
+
export class iBot extends EventEmitter {
|
|
7
|
+
config;
|
|
8
|
+
db;
|
|
9
|
+
constructor(cfg, db) {
|
|
10
|
+
super();
|
|
11
|
+
this.config = cfg;
|
|
12
|
+
this.db = db;
|
|
13
|
+
}
|
|
14
|
+
isEditTooLate = (timestamp) => {
|
|
15
|
+
const expiration = this.config.editMaxAge ?? DEFAULT_MAX_EDIT_AGE;
|
|
16
|
+
if (expiration <= 0) {
|
|
17
|
+
return expiration === 0;
|
|
18
|
+
}
|
|
19
|
+
return dayjs(timestamp).add(expiration, 'ms').isBefore(dayjs());
|
|
20
|
+
};
|
|
21
|
+
createListeners(bots) {
|
|
22
|
+
bots.forEach(b => {
|
|
23
|
+
if (b === this) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
b.on('message', this.sendMessage);
|
|
27
|
+
b.on('react', this.sendReact);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
insertEmoji = async (message) => {
|
|
31
|
+
const guild = await this.getGuild();
|
|
32
|
+
const emojiMatches = [];
|
|
33
|
+
let startStr = -1;
|
|
34
|
+
message.split('').forEach((ch, i) => {
|
|
35
|
+
if (ch === '>' && startStr >= 0) {
|
|
36
|
+
const match = message.substring(startStr, i + 1);
|
|
37
|
+
if (!emojiMatches.some(m => m.match === match)) {
|
|
38
|
+
emojiMatches.push({ match });
|
|
39
|
+
}
|
|
40
|
+
startStr = -1;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (ch === '<' && startStr === -1) {
|
|
44
|
+
startStr = i;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (startStr === -1) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!EMOJI_CHARS.test(ch)) {
|
|
51
|
+
startStr = -1;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
emojiMatches.forEach(m => {
|
|
55
|
+
const tokens = m.match.substring(1, m.match.length - 1).split(':');
|
|
56
|
+
if (tokens.length !== 3) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const emoji = this.findEmoji(guild, tokens[1]);
|
|
60
|
+
m.replacement = emoji ? `${emoji.animated ? '<a' : '<'}:${emoji.name}:${emoji.id}>` : `:${tokens[1]}:`;
|
|
61
|
+
});
|
|
62
|
+
return emojiMatches.reduce((accumulator, m) => m.replacement ? accumulator.replaceAll(m.match, m.replacement) : accumulator, message);
|
|
63
|
+
};
|
|
64
|
+
upsert = (payload, destination, destinationid) => {
|
|
65
|
+
this.db.upsert({
|
|
66
|
+
source: payload.source,
|
|
67
|
+
sourceid: payload.originatingId,
|
|
68
|
+
destination,
|
|
69
|
+
destinationid
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
getDestination = (payload, destination) => {
|
|
73
|
+
return this.db.getMessage({
|
|
74
|
+
source: payload.source,
|
|
75
|
+
sourceid: payload.originatingId,
|
|
76
|
+
destination,
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
getSource = (query) => {
|
|
80
|
+
return this.db.getSource(query);
|
|
81
|
+
};
|
|
82
|
+
deleteDestination = (payload, destination) => {
|
|
83
|
+
return this.db.deleteMessage({
|
|
84
|
+
source: payload.source,
|
|
85
|
+
sourceid: payload.originatingId,
|
|
86
|
+
destination
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=bot.interface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.interface.js","sourceRoot":"","sources":["../src/bot.interface.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getRestOptions, Service } from './rest.js';
|
|
2
|
+
import { DiscordBot } from './bot.discord.js';
|
|
3
|
+
import { FluxerBot } from './bot.fluxer.js';
|
|
4
|
+
export async function createBot(cfg, db) {
|
|
5
|
+
const endpoints = await getRestOptions(cfg.server);
|
|
6
|
+
if (endpoints.type === Service.Discord) {
|
|
7
|
+
return new DiscordBot(cfg, db);
|
|
8
|
+
}
|
|
9
|
+
return new FluxerBot(cfg, db);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=bot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAc,EAAE,EAAM;IAClD,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,OAAO,IAAI,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,IAAI,SAAS,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const TABLE = 'packages';
|
|
2
|
+
export function migrate(db) {
|
|
3
|
+
const migrations = [
|
|
4
|
+
[
|
|
5
|
+
'CREATE TABLE IF NOT EXISTS messages (source VARCHAR(128), sourceid VARCHAR(128), destination VARCHAR(128), destinationid VARCHAR(128), PRIMARY KEY (source, sourceid, destination));',
|
|
6
|
+
'CREATE INDEX IF NOT EXISTS reverse_index ON messages (destination, destinationid)'
|
|
7
|
+
],
|
|
8
|
+
];
|
|
9
|
+
const version = db.pragma('user_version')[0]?.user_version ?? 0;
|
|
10
|
+
const migrationsTodo = migrations.slice(version);
|
|
11
|
+
const runMigration = db.transaction((migrations) => {
|
|
12
|
+
migrations.forEach(m => db.exec(m));
|
|
13
|
+
});
|
|
14
|
+
migrationsTodo.forEach((m, i) => {
|
|
15
|
+
console.log(`Running DB migration ${i}`);
|
|
16
|
+
runMigration(m);
|
|
17
|
+
});
|
|
18
|
+
db.pragma(`user_version=${migrations.length}`);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=db-migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-migrations.js","sourceRoot":"","sources":["../src/db-migrations.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAAC;AAEhC,MAAM,UAAU,OAAO,CAAC,EAAY;IAChC,MAAM,UAAU,GAAe;QAC3B;YACI,sLAAsL;YACtL,mFAAmF;SACtF;KACJ,CAAC;IAEF,MAAM,OAAO,GAAI,EAAE,CAAC,MAAM,CAAC,cAAc,CAAkC,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,CAAC,CAAC;IAClG,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,UAAoB,EAAE,EAAE;QACzD,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IACH,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC5B,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC;QACzC,YAAY,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,CAAC,gBAAgB,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface DBConfig {
|
|
2
|
+
file: string;
|
|
3
|
+
skipMigrations: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface MessageKey {
|
|
6
|
+
source: string;
|
|
7
|
+
sourceid: string;
|
|
8
|
+
destination: string;
|
|
9
|
+
}
|
|
10
|
+
export interface MessageDestinationId {
|
|
11
|
+
destinationid: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ReverseKey {
|
|
14
|
+
destination: string;
|
|
15
|
+
destinationid: string;
|
|
16
|
+
}
|
|
17
|
+
export type Message = MessageDestinationId & MessageKey;
|
|
18
|
+
export declare class DB {
|
|
19
|
+
private db;
|
|
20
|
+
constructor(cfg: Partial<DBConfig>);
|
|
21
|
+
getMessage: (query: MessageKey) => string | undefined;
|
|
22
|
+
getSource: (query: ReverseKey) => Message | undefined;
|
|
23
|
+
upsert: (data: Message) => void;
|
|
24
|
+
deleteMessage: (key: MessageKey) => void;
|
|
25
|
+
close: () => void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
import { migrate } from './db-migrations.js';
|
|
4
|
+
export class DB {
|
|
5
|
+
db;
|
|
6
|
+
constructor(cfg) {
|
|
7
|
+
const db = this.db = new Database(cfg.file ?? path.join('db', 'messages.db'));
|
|
8
|
+
cfg.skipMigrations || migrate(db);
|
|
9
|
+
}
|
|
10
|
+
getMessage = (query) => {
|
|
11
|
+
const statement = this.db.prepare('SELECT destinationid FROM messages WHERE source = @source AND sourceid = @sourceid AND destination = @destination;');
|
|
12
|
+
return statement.get(query)?.destinationid;
|
|
13
|
+
};
|
|
14
|
+
getSource = (query) => {
|
|
15
|
+
const statement = this.db.prepare('SELECT * FROM messages WHERE destination = @destination AND destinationid = @destinationid;');
|
|
16
|
+
return statement.get(query);
|
|
17
|
+
};
|
|
18
|
+
upsert = (data) => {
|
|
19
|
+
const statement = this.db.prepare('INSERT INTO messages (source, sourceid, destination, destinationid) VALUES(@source, @sourceid, @destination, @destinationid) ON CONFLICT(source, sourceid, destination) DO UPDATE SET destinationid=excluded.destinationid');
|
|
20
|
+
statement.run(data);
|
|
21
|
+
};
|
|
22
|
+
deleteMessage = (key) => {
|
|
23
|
+
const statement = this.db.prepare('DELETE FROM messages WHERE source = @source AND sourceid = @sourceid AND destination = @destination;');
|
|
24
|
+
statement.run(key);
|
|
25
|
+
};
|
|
26
|
+
close = () => {
|
|
27
|
+
this.db.close();
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAwB7C,MAAM,OAAO,EAAE;IACH,EAAE,CAAoB;IAE9B,YAAY,GAAsB;QAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC;QAC9E,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,UAAU,GAAG,CAAC,KAAiB,EAAE,EAAE;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAmC,oHAAoH,CAAC,CAAC;QAC1L,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/C,CAAC,CAAA;IAED,SAAS,GAAG,CAAC,KAAiB,EAAE,EAAE;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAsB,6FAA6F,CAAC,CAAC;QACtJ,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,CAAA;IAED,MAAM,GAAG,CAAC,IAAa,EAAE,EAAE;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACb,4NAA4N,CAAC,CAAC;QAClP,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAA;IAED,aAAa,GAAG,CAAC,GAAe,EAAE,EAAE;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAmB,sGAAsG,CAAC,CAAC;QAC5J,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC,CAAA;IAED,KAAK,GAAG,GAAG,EAAE;QACT,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAA;CACJ"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { parse } from 'yaml';
|
|
2
|
+
import fsp from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { DB } from './db.js';
|
|
5
|
+
import { createBot } from './bot.js';
|
|
6
|
+
async function readYaml(cfg) {
|
|
7
|
+
try {
|
|
8
|
+
const configBody = await fsp.readFile(cfg, { encoding: 'utf-8' });
|
|
9
|
+
return parse(configBody);
|
|
10
|
+
}
|
|
11
|
+
catch (ex) {
|
|
12
|
+
console.error(`Failed to load ${cfg}`);
|
|
13
|
+
throw ex;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function initialize() {
|
|
17
|
+
const config = await readYaml(process.env['BRIDGE_CONFIG'] ?? path.join('config', 'config.yaml'));
|
|
18
|
+
const db = new DB(config.db ?? {});
|
|
19
|
+
const bots = await Promise.all((config.bots ?? []).map(botCfg => createBot(botCfg, db)));
|
|
20
|
+
await Promise.all(bots.map(bot => bot.init()));
|
|
21
|
+
bots.forEach(b => b.createListeners(bots));
|
|
22
|
+
return bots;
|
|
23
|
+
}
|
|
24
|
+
export async function main() {
|
|
25
|
+
const bots = await initialize();
|
|
26
|
+
process.on('SIGTERM', () => {
|
|
27
|
+
bots.forEach(b => b.close());
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,GAAG,MAAM,aAAa,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,EAAE,EAAiB,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGrC,KAAK,UAAU,QAAQ,CAAI,GAAW;IAClC,IAAI,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAClE,OAAO,KAAK,CAAC,UAAU,CAAM,CAAC;IAClC,CAAC;IACD,OAAO,EAAE,EAAE,CAAC;QACR,OAAO,CAAC,KAAK,CAAC,kBAAkB,GAAG,EAAE,CAAC,CAAC;QACvC,MAAM,EAAE,CAAC;IACb,CAAC;AACL,CAAC;AAOD,KAAK,UAAU,UAAU;IACrB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAe,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;IAChH,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,MAAM,IAAI,GAAqB,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3G,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/C,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IAE3C,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI;IACtB,MAAM,IAAI,GAAG,MAAM,UAAU,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACvB,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AAEhC,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import ky from 'ky';
|
|
2
|
+
export var Service;
|
|
3
|
+
(function (Service) {
|
|
4
|
+
Service[Service["Discord"] = 0] = "Discord";
|
|
5
|
+
Service[Service["Fluxer"] = 1] = "Fluxer";
|
|
6
|
+
})(Service || (Service = {}));
|
|
7
|
+
async function tryRequest(url) {
|
|
8
|
+
try {
|
|
9
|
+
return await ky.get(url).json();
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function getRestOptions(server) {
|
|
16
|
+
if (!server || server.toLowerCase().startsWith('https://discord.com')) {
|
|
17
|
+
return {
|
|
18
|
+
type: Service.Discord,
|
|
19
|
+
rest: {}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const result = await tryRequest(new URL('.well-known/fluxer', server).href);
|
|
23
|
+
if (result) {
|
|
24
|
+
return {
|
|
25
|
+
type: Service.Fluxer,
|
|
26
|
+
rest: {
|
|
27
|
+
api: result.endpoints.api,
|
|
28
|
+
cdn: result.endpoints.static_cdn,
|
|
29
|
+
mediaProxy: result.endpoints.media,
|
|
30
|
+
version: `v${result.api_code_version}`
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
throw new Error(`Server ${server} does not seem to be supported by djs-bridge at this time.`);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=rest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rest.js","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAqBpB,MAAM,CAAN,IAAY,OAGX;AAHD,WAAY,OAAO;IACf,2CAAO,CAAA;IACP,yCAAM,CAAA;AACV,CAAC,EAHW,OAAO,KAAP,OAAO,QAGlB;AAED,KAAK,UAAU,UAAU,CAAI,GAAW;IACpC,IAAI,CAAC;QACD,OAAO,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;IACnC,CAAC;IACD,MAAM,CAAC;QACH,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAe;IAChD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAAE,CAAC;QACpE,OAAO;YACH,IAAI,EAAE,OAAO,CAAC,OAAO;YACrB,IAAI,EAAE,EAAE;SACX,CAAC;IACN,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAkB,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7F,IAAI,MAAM,EAAE,CAAC;QACT,OAAO;YACH,IAAI,EAAE,OAAO,CAAC,MAAM;YACpB,IAAI,EAAE;gBACF,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG;gBACzB,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC,UAAU;gBAChC,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,KAAK;gBAClC,OAAO,EAAE,IAAI,MAAM,CAAC,gBAAgB,EAAE;aACzC;SACJ,CAAA;IACL,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,4DAA4D,CAAC,CAAC;AAClG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function notStupidParseInt(v: string | undefined | number): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,CAA8B;IAC5D,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QACxB,OAAO,CAAC,CAAC;IACb,CAAC;IACD,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "djs-bridge",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Yet another Discord ↔️ Fluxer bridge application",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"discord",
|
|
7
|
+
"fluxer",
|
|
8
|
+
"chat"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://codeberg.org/sanin/djs-bridge",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://codeberg.org/sanin/djs-bridge/issues"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://codeberg.org/sanin/djs-bridge.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Cory Sanin",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./distribution/index.d.ts",
|
|
24
|
+
"import": "./distribution/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"main": "./distribution/index.js",
|
|
28
|
+
"types": "./distribution/index.d.ts",
|
|
29
|
+
"bin": {
|
|
30
|
+
"djs-bridge": "./distribution/main.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"distribution"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@fluxerjs/core": "^3.3.0",
|
|
37
|
+
"better-sqlite3": "^13.0.3",
|
|
38
|
+
"dayjs": "^1.11.23",
|
|
39
|
+
"discord.js": "^14.28.1",
|
|
40
|
+
"ky": "^2.2.0",
|
|
41
|
+
"yaml": "^2.9.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@sindresorhus/tsconfig": "9.0.0",
|
|
45
|
+
"@types/better-sqlite3": "^9.6.0",
|
|
46
|
+
"@types/node": "^26.6.6",
|
|
47
|
+
"typescript": "7.0.2"
|
|
48
|
+
},
|
|
49
|
+
"devEngines": {
|
|
50
|
+
"packageManager": {
|
|
51
|
+
"name": "pnpm",
|
|
52
|
+
"version": "12.18.0",
|
|
53
|
+
"onFail": "download"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc && chmod +x ./distribution/main.js",
|
|
58
|
+
"start": "node distribution/main.js"
|
|
59
|
+
}
|
|
6
60
|
}
|