3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-21 00:35:02 +00:00

feat: AFK Command

This commit is contained in:
Jonathan 2021-04-02 23:09:12 -04:00
parent 7f75d6d8d3
commit b6c822e826
No known key found for this signature in database
GPG key ID: 19B04E3CBE0885B1
10 changed files with 262 additions and 0 deletions

31
backend/src/data/AFK.ts Normal file
View 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 });
}
}

View 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;
}

View 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");
}
}

View 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();
}
})

View 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,
});
}
})

View 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);
}
});

View 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!`,
});
}

View 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!";
}

View 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>();

View file

@ -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