diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..80f30ba --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,166 @@ +name: Docker + +on: + push: + branches: + - "master" + tags: + - "*" + workflow_dispatch: + +jobs: + publish_api: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + # list of Docker images to use as base name for tags + images: ghcr.io/janderedev/automod-api + # generate Docker tags based on branch and short commit SHA + tags: | + type=ref,event=branch + type=sha + - + name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - + name: Login to DockerHub + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push - API + uses: docker/build-push-action@v3 + with: + context: ./api + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + publish_web: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + # list of Docker images to use as base name for tags + images: ghcr.io/janderedev/automod-web + # generate Docker tags based on branch and short commit SHA + tags: | + type=ref,event=branch + type=sha + - + name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - + name: Login to DockerHub + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push - Web + uses: docker/build-push-action@v3 + with: + context: ./web + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + publish_bridge: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + # list of Docker images to use as base name for tags + images: ghcr.io/janderedev/automod-bridge + # generate Docker tags based on branch and short commit SHA + tags: | + type=ref,event=branch + type=sha + - + name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - + name: Login to DockerHub + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push - Bridge + uses: docker/build-push-action@v3 + with: + context: ./bridge + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + publish_bot: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + # list of Docker images to use as base name for tags + images: ghcr.io/janderedev/automod-bot + # generate Docker tags based on branch and short commit SHA + tags: | + type=ref,event=branch + type=sha + - + name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - + name: Login to DockerHub + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push - Bot + uses: docker/build-push-action@v3 + with: + context: ./bot + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/bot/package.json b/bot/package.json index d67c0b3..b7653e2 100644 --- a/bot/package.json +++ b/bot/package.json @@ -13,7 +13,7 @@ "author": "", "license": "ISC", "dependencies": { - "@janderedev/revolt.js": "^6.0.0-patch.3", + "@janderedev/revolt.js": "^6.0.0-2-patch.1", "@types/monk": "^6.0.0", "axios": "^0.22.0", "dayjs": "^1.10.7", diff --git a/bot/src/bot/commands/moderation/purge.ts b/bot/src/bot/commands/moderation/purge.ts index ab263aa..66dbb7b 100644 --- a/bot/src/bot/commands/moderation/purge.ts +++ b/bot/src/bot/commands/moderation/purge.ts @@ -26,7 +26,8 @@ export default { if (amount > MAX_PURGE_AMOUNT) return message.reply(`Message count exceeds the limit of ${MAX_PURGE_AMOUNT}.`); messages = await message.channel!.fetchMessages({ - limit: amount + limit: amount, + before: message._id, }); } // delete messages between [id] and [id] @@ -78,15 +79,21 @@ export default { messages = messages.filter(m => users.find(u => u?._id == m.author_id)); } - let m = await (message.channel?.sendMessage(`Deleting ${messages.length} messages...`)?.catch(console.error)); - let res = await Promise.allSettled(messages.map(m => m.delete())); + await message.channel?.deleteMessages(messages.map(m => m._id)); - let failures = res.filter(r => r.status == 'rejected').length; - - await m?.edit({ content: `Deleted ${messages.length} messages.` - + `${failures > 0 ? `\n${failures} message${failures == 1 ? '' : 's'} failed to delete.` : ''}` }) + const replyMsg = await message.channel?.sendMessage({ content: `Deleted ${messages.length} messages.` }) .catch(console.error); + + setTimeout(async () => { + try { + await message.channel?.deleteMessages([ + replyMsg!._id, + message._id, + ]); + } catch(e) { console.error(e) } + }, 6000); } catch(e) { + console.error(e); message.channel?.sendMessage(`An error has occurred: ${e}`); } } diff --git a/bot/src/bot/modules/mod_logs.ts b/bot/src/bot/modules/mod_logs.ts index 0789e66..c23adcb 100644 --- a/bot/src/bot/modules/mod_logs.ts +++ b/bot/src/bot/modules/mod_logs.ts @@ -42,9 +42,6 @@ client.on('packet', async (packet) => { discord: { description: `Author: @${m?.author?.username || m?.author_id || "Unknown"} | Channel: ${channel?.name || channel?._id}` }, - revoltRvembed: { - description: `Author: @${m?.author?.username || m?.author_id || "Unknown"} | Channel: ${channel?.name || channel?._id}` - } } } @@ -88,9 +85,6 @@ client.on('packet', async (packet) => { discord: { description: `Author: @${message.author?.username || message.author_id} | Channel: ${message.channel?.name || message.channel_id}` }, - revoltRvembed: { - description: `Author: @${message.author?.username || message.author_id} | Channel: ${message.channel?.name || message.channel_id}` - } } } @@ -112,6 +106,33 @@ client.on('packet', async (packet) => { console.error(e); } } + + if (packet.type == 'BulkMessageDelete') { + const channel = client.channels.get(packet.channel); + if (!channel) return; + + try { + let config = await dbs.SERVERS.findOne({ id: channel.server?._id }); + if (config?.logs?.messageUpdate) { + let embed: LogMessage = { + title: `Bulk delete in in ${channel.server?.name}`, + description: `${packet.ids.length} messages deleted in ` + + `[#${channel.name}](/server/${channel.server_id}/channel/${channel._id})`, + fields: [], + color: '#ff392b', + overrides: { + discord: { + description: `${packet.ids.length} messages deleted in #${channel.name}`, + } + } + } + + await sendLogMessage(config.logs.messageUpdate, embed); + } + } catch(e) { + console.error(e); + } + } }); async function logModAction(type: 'warn'|'kick'|'ban'|'votekick', server: Server, mod: Member, target: string, reason: string|null, infractionID: string, extraText?: string): Promise { @@ -132,15 +153,7 @@ async function logModAction(type: 'warn'|'kick'|'ban'|'votekick', server: Server + `**Warn ID**: \`${infractionID}\`\n` + (extraText ?? ''), color: embedColor, - overrides: { - revoltRvembed: { - description: `@${mod.user?.username} ${aType} ` - + `${await fetchUsername(target)}${type == 'warn' ? '.' : ` from ${server.name}.`}\n` - + `Reason: ${reason ? reason : 'No reason provided.'}\n` - + `Warn ID: ${infractionID}\n` - + (extraText ?? ''), - } - } + overrides: {}, }); } } catch(e) { diff --git a/bot/src/bot/util.ts b/bot/src/bot/util.ts index a13c5a9..b5be848 100644 --- a/bot/src/bot/util.ts +++ b/bot/src/bot/util.ts @@ -218,36 +218,23 @@ async function sendLogMessage(config: LogConfig, content: LogMessage) { const channel = client.channels.get(config.revolt.channel) || await client.channels.fetch(config.revolt.channel); let message = ''; + let embed: SendableEmbed|undefined = undefined; switch(config.revolt.type) { - case 'RVEMBED': - case 'DYNAMIC': - c = { ...c, ...content.overrides?.revoltRvembed }; - let url = `https://rvembed.janderedev.xyz/embed`; - let args = []; + case 'EMBED': + c = { ...c, ...content.overrides?.revoltEmbed }; + embed = { + title: c.title, + description: c.description, + colour: c.color, + } - let description = (c.description ?? ''); if (c.fields?.length) { for (const field of c.fields) { - description += `\n${field.title}\n` + - `${field.content}`; + embed.description += `\n#### ${field.title}\n${field.content}`; } } + break; - description = description.trim(); - - if (c.title) args.push(`title=${encodeURIComponent(c.title)}`); - if (description) args.push(`description=${encodeURIComponent(description)}`); - if (c.color) args.push(`color=${encodeURIComponent(c.color)}`); - if (c.image) { - args.push(`image=${encodeURIComponent(c.image.url)}`); - args.push(`image_large=true`); - } - - if (!(config.revolt.type == 'DYNAMIC' && (description.length > 1000 || description.split('\n').length > 6))) { - for (const i in args) url += `${i == '0' ? '?' : '&'}${args[i]}`; - message = `[\u200b](${url})`; - break; - } default: // QUOTEBLOCK, PLAIN or unspecified // please disregard this mess @@ -271,6 +258,7 @@ async function sendLogMessage(config: LogConfig, content: LogMessage) { channel.sendMessage({ content: message, + embeds: embed ? [ embed ] : undefined, attachments: content.attachments ? await Promise.all(content.attachments?.map(a => uploadFile(a.content, a.name))) : undefined diff --git a/bot/src/struct/LogConfig.ts b/bot/src/struct/LogConfig.ts index 69fafef..8cb46e5 100644 --- a/bot/src/struct/LogConfig.ts +++ b/bot/src/struct/LogConfig.ts @@ -2,11 +2,9 @@ export default class LogConfig { revolt?: { channel?: string, - // RVEMBED uses https://rvembed.janderedev.xyz to send a discord style embed, which doesn't - // work properly with longer messages. + // EMBED uses Revolt's embeds. // PLAIN is like QUOTEBLOCK but without the quotes. - // DYNAMIC uses RVEMBED if the message is short enough, otherwise defaults to QUOTEBLOCK. - type?: 'QUOTEBLOCK'|'PLAIN'|'RVEMBED'|'DYNAMIC'; + type?: 'EMBED'|'QUOTEBLOCK'|'PLAIN'; } discord?: { webhookUrl?: string, diff --git a/bot/src/struct/LogMessage.ts b/bot/src/struct/LogMessage.ts index b203228..0ae6784 100644 --- a/bot/src/struct/LogMessage.ts +++ b/bot/src/struct/LogMessage.ts @@ -11,7 +11,7 @@ export default class LogMessage { attachments?: { name: string, content: Buffer }[]; overrides?: { // These take priority over `revolt` - revoltRvembed?: Override, + revoltEmbed?: Override, revoltQuoteblock?: Override, revolt?: Override, diff --git a/bot/yarn.lock b/bot/yarn.lock index b8d0645..e7ad97e 100644 --- a/bot/yarn.lock +++ b/bot/yarn.lock @@ -47,10 +47,10 @@ axios "^0.26.1" openapi-typescript "^5.2.0" -"@janderedev/revolt.js@^6.0.0-patch.3": - version "6.0.0-patch.3" - resolved "https://registry.yarnpkg.com/@janderedev/revolt.js/-/revolt.js-6.0.0-patch.3.tgz#7d9ad66e5a0d54fb2f5f0f8887cbd1382c69d301" - integrity sha512-aZ1vubm8+l10lTy5HwO3vAc7E2/bm3+hfFhNqU7l+9QKecIAm6f45p7RNC19afSYChIZiyhtGPtWTuVJ9pa0RA== +"@janderedev/revolt.js@^6.0.0-2-patch.1": + version "6.0.0-2-patch.1" + resolved "https://registry.yarnpkg.com/@janderedev/revolt.js/-/revolt.js-6.0.0-2-patch.1.tgz#c0e1b5004c2d212ceb4167c628de58ccb278b469" + integrity sha512-DxC9BlGgtx6D0Vb/Cvl5IPJJ7IEPtaD+FDuM1WHEvUUDBTcRZsaSXD/6EmzuejdXRVO9FEYKq1UMhLsX7TIDGg== dependencies: "@insertish/exponential-backoff" "3.1.0-patch.2" "@insertish/isomorphic-ws" "^4.0.1" @@ -61,7 +61,7 @@ lodash.isequal "^4.5.0" long "^5.2.0" mobx "^6.3.2" - revolt-api "0.5.3" + revolt-api "0.5.3-5-patch.3" ulid "^2.3.0" ws "^8.2.2" @@ -600,10 +600,10 @@ require-at@^1.0.6: resolved "https://registry.yarnpkg.com/require-at/-/require-at-1.0.6.tgz#9eb7e3c5e00727f5a4744070a7f560d4de4f6e6a" integrity sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g== -revolt-api@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3.tgz#e0ec2dcf812ea4338247b2eb77d67fc731d71b8a" - integrity sha512-hYdyStQiDZFvD+0dlf6SgQSiOk+JiEmQo0qIQHaqYRtrFN6FQBbGVNaiv7b5LzHHMPq7vks6ZVVA7hSNpcwlkA== +revolt-api@0.5.3-5-patch.3: + version "0.5.3-5-patch.3" + resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-5-patch.3.tgz#73a0614a02a210adac2b946fc0fc35f4304fb348" + integrity sha512-xESgQ9kp8T5iupeixt/yJIiGDiM11pm1b2E6srM/+jrV7Jyrj/vPgDPv3hEeJFhNmUK6EP5tKxkmTBhcqNE/Vw== dependencies: "@insertish/oapi" "0.1.15" axios "^0.26.1"