mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-21 16:55:03 +00:00
feat: AFK Command
This commit is contained in:
parent
7f75d6d8d3
commit
b6c822e826
10 changed files with 262 additions and 0 deletions
31
backend/src/data/AFK.ts
Normal file
31
backend/src/data/AFK.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { BaseRepository } from "./BaseRepository";
|
||||
import { getRepository, Repository } from "typeorm";
|
||||
import { AFK as AFKEntity } from "./entities/AFK";
|
||||
|
||||
export class AFK extends BaseRepository {
|
||||
private afk: Repository<AFKEntity>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.afk = getRepository(AFKEntity);
|
||||
}
|
||||
|
||||
async getUserAFKStatus(user_id: string) {
|
||||
return await this.afk.findOne({ user_id });
|
||||
}
|
||||
|
||||
async setAfkStatus(user_id: string, status: string) {
|
||||
const settings = new AFKEntity();
|
||||
settings.user_id = user_id;
|
||||
settings.status = status;
|
||||
|
||||
return await this.afk.save(settings);
|
||||
}
|
||||
|
||||
async clearAFKStatus(user_id: string) {
|
||||
const afk = await this.afk.findOne({ user_id });
|
||||
if (!afk) return;
|
||||
|
||||
return await this.afk.delete({ user_id });
|
||||
}
|
||||
}
|
14
backend/src/data/entities/AFK.ts
Normal file
14
backend/src/data/entities/AFK.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Column, Entity, PrimaryColumn } from "typeorm";
|
||||
|
||||
@Entity("afk")
|
||||
export class AFK {
|
||||
@Column()
|
||||
@PrimaryColumn()
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
user_id: string;
|
||||
|
||||
@Column()
|
||||
status: string;
|
||||
}
|
34
backend/src/migrations/1617410382003-AFKStatus.ts
Normal file
34
backend/src/migrations/1617410382003-AFKStatus.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { MigrationInterface, QueryRunner, Table } from "typeorm";
|
||||
|
||||
export class AFKStatus1617410382003 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: "afk",
|
||||
columns: [
|
||||
{
|
||||
name: "id",
|
||||
type: "int",
|
||||
isPrimary: true,
|
||||
isGenerated: true,
|
||||
generationStrategy: "increment",
|
||||
},
|
||||
{
|
||||
name: "user_id",
|
||||
type: "bigint",
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
type: "varchar",
|
||||
length: "255",
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
return queryRunner.dropTable("afk");
|
||||
}
|
||||
}
|
46
backend/src/plugins/AFK/AFKPlugin.ts
Normal file
46
backend/src/plugins/AFK/AFKPlugin.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { PluginOptions } from "knub";
|
||||
import { AFKPluginType, ConfigSchema } from './types';
|
||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { AFK } from "src/data/AFK";
|
||||
|
||||
import { AfkSetCmd } from "./commands/AFKCmd";
|
||||
import { AFKNotificationEvt } from "./events/AFKNotificationEvt";
|
||||
|
||||
const defaultOptions: PluginOptions<AFKPluginType> = {
|
||||
config: {
|
||||
can_afk: false,
|
||||
allow_links: false,
|
||||
allow_invites: false,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
level: '>=50',
|
||||
config: {
|
||||
can_afk: true,
|
||||
allow_links: true,
|
||||
allow_invites: true,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const AFKPlugin = zeppelinGuildPlugin<AFKPluginType>()("afk", {
|
||||
showInDocs: true,
|
||||
info: {
|
||||
prettyName: "AFK",
|
||||
description: "Allows you to set your AFK Status.",
|
||||
},
|
||||
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
commands: [AfkSetCmd],
|
||||
events: [AFKNotificationEvt],
|
||||
|
||||
onLoad(pluginData) {
|
||||
const { state } = pluginData;
|
||||
|
||||
state.afkUsers = new AFK();
|
||||
}
|
||||
})
|
||||
|
46
backend/src/plugins/AFK/commands/AFKCmd.ts
Normal file
46
backend/src/plugins/AFK/commands/AFKCmd.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { afkCmd } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { parseStatusMessage } from "../functions/parseStatusMessage";
|
||||
|
||||
export const AfkSetCmd = afkCmd({
|
||||
trigger: ['afk', 'afk set'],
|
||||
permission: 'can_afk',
|
||||
|
||||
signature: {
|
||||
status: ct.string({ rest: true, required: true }),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
// Checks if the user is AFK, if so, return.
|
||||
const isAfk = await pluginData.state.afkUsers.getUserAFKStatus(msg.author.id);
|
||||
if (isAfk) return;
|
||||
|
||||
const status = args.status.join(" ");
|
||||
|
||||
// Check status length
|
||||
if (status.length > 124) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Status length is above **124** characters.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Checks status based on configuration options
|
||||
const parsed = parseStatusMessage(pluginData, msg.member, status);
|
||||
if (typeof parsed === 'string') {
|
||||
sendErrorMessage(pluginData, msg.channel, parsed);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set user status
|
||||
const afk = await pluginData.state.afkUsers.setAfkStatus(
|
||||
msg.author.id,
|
||||
status,
|
||||
);
|
||||
|
||||
sendSuccessMessage(pluginData, msg.channel, `AFK Status set to: **${afk.status}**`, {
|
||||
roles: false,
|
||||
everyone: false,
|
||||
users: false,
|
||||
});
|
||||
}
|
||||
})
|
28
backend/src/plugins/AFK/events/AFKNotificationEvt.ts
Normal file
28
backend/src/plugins/AFK/events/AFKNotificationEvt.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { sendUserMentionMessage, sendWelcomeBackMessage } from "../functions/buildAFKMessages";
|
||||
import { afkEvt } from "../types";
|
||||
|
||||
export const AFKNotificationEvt = afkEvt({
|
||||
event: 'messageCreate',
|
||||
|
||||
listener: async ({ pluginData, args: { message } }) => {
|
||||
// Mention Check (if someone mentions the AFK user)
|
||||
if (message.mentions.length) {
|
||||
const afk = await pluginData.state.afkUsers.getUserAFKStatus(message.mentions[0].id);
|
||||
if (!afk) return;
|
||||
|
||||
sendUserMentionMessage(message, afk.status);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Self AFK Check (if user is the one that's AFK)
|
||||
const afk = await pluginData.state.afkUsers.getUserAFKStatus(message.author.id);
|
||||
if (!afk) return;
|
||||
|
||||
try {
|
||||
await pluginData.state.afkUsers.clearAFKStatus(message.author.id);
|
||||
} catch (err) {}
|
||||
|
||||
sendWelcomeBackMessage(message);
|
||||
}
|
||||
});
|
23
backend/src/plugins/AFK/functions/buildAFKMessages.ts
Normal file
23
backend/src/plugins/AFK/functions/buildAFKMessages.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { Message } from "eris";
|
||||
|
||||
export function sendUserMentionMessage(message: Message, status: string) {
|
||||
return message.channel.createMessage({
|
||||
allowedMentions: {
|
||||
users: [message.author.id],
|
||||
everyone: false,
|
||||
roles: false,
|
||||
},
|
||||
content: `<@!${message.author.id}>, the user mentioned is currently AFK: **${status}**`,
|
||||
});
|
||||
}
|
||||
|
||||
export function sendWelcomeBackMessage(message: Message) {
|
||||
return message.channel.createMessage({
|
||||
allowedMentions: {
|
||||
users: [message.author.id],
|
||||
everyone: false,
|
||||
roles: false,
|
||||
},
|
||||
content: `<@!${message.author.id}>, welcome back!`,
|
||||
});
|
||||
}
|
18
backend/src/plugins/AFK/functions/parseStatusMessage.ts
Normal file
18
backend/src/plugins/AFK/functions/parseStatusMessage.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Member } from "eris";
|
||||
import { GuildPluginData } from "knub";
|
||||
import { hasPermission } from "src/pluginUtils";
|
||||
import { AFKPluginType } from "../types";
|
||||
|
||||
// https://github.com/sapphire-project/utilities/blob/main/packages/discord-utilities/src/lib/regexes.ts#L58
|
||||
const HttpUrlRegex = /^https?:\/\//;
|
||||
|
||||
// https://github.com/sapphire-project/utilities/blob/main/packages/discord-utilities/src/lib/regexes.ts#L25
|
||||
const DiscordInviteLinkRegex = /^(?:https?:\/\/)?(?:www\.)?(?:discord\.gg\/|discord(?:app)?\.com\/invite\/)?(?<code>[\w\d-]{2,})$/i;
|
||||
|
||||
export function parseStatusMessage(pluginData: GuildPluginData<AFKPluginType>, member: Member, status: string) {
|
||||
const allow_links = hasPermission(pluginData, "allow_links", { member });
|
||||
const allow_invites = hasPermission(pluginData, "allow_invites", { member });
|
||||
|
||||
if (!allow_links && HttpUrlRegex.test(status)) return "Links are not allowed in an AFK status!";
|
||||
if (!allow_invites && DiscordInviteLinkRegex.test(status)) return "Invites are not allowed in an AFK status!";
|
||||
}
|
20
backend/src/plugins/AFK/types.ts
Normal file
20
backend/src/plugins/AFK/types.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import * as t from 'io-ts';
|
||||
import { BasePluginType, guildCommand, guildEventListener } from 'knub';
|
||||
import { AFK } from '../../data/AFK';
|
||||
|
||||
export const ConfigSchema = t.type({
|
||||
can_afk: t.boolean,
|
||||
allow_links: t.boolean,
|
||||
allow_invites: t.boolean,
|
||||
});
|
||||
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||
|
||||
export interface AFKPluginType extends BasePluginType {
|
||||
config: TConfigSchema;
|
||||
state: {
|
||||
afkUsers: AFK;
|
||||
}
|
||||
}
|
||||
|
||||
export const afkCmd = guildCommand<AFKPluginType>();
|
||||
export const afkEvt = guildEventListener<AFKPluginType>();
|
|
@ -33,6 +33,7 @@ import { BotControlPlugin } from "./BotControl/BotControlPlugin";
|
|||
import { GuildAccessMonitorPlugin } from "./GuildAccessMonitor/GuildAccessMonitorPlugin";
|
||||
import { TimeAndDatePlugin } from "./TimeAndDate/TimeAndDatePlugin";
|
||||
import { CountersPlugin } from "./Counters/CountersPlugin";
|
||||
import { AFKPlugin } from './AFK/AFKPlugin';
|
||||
|
||||
// prettier-ignore
|
||||
export const guildPlugins: Array<ZeppelinGuildPluginBlueprint<any>> = [
|
||||
|
@ -67,6 +68,7 @@ export const guildPlugins: Array<ZeppelinGuildPluginBlueprint<any>> = [
|
|||
CustomEventsPlugin,
|
||||
TimeAndDatePlugin,
|
||||
CountersPlugin,
|
||||
AFKPlugin,
|
||||
];
|
||||
|
||||
// prettier-ignore
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue