Fully functional LocatePlugin with alerts in DB and reminders
This commit is contained in:
parent
b230a73a6f
commit
913120a1fe
6 changed files with 284 additions and 2 deletions
|
@ -11,11 +11,11 @@ export class PluginRuntimeError {
|
||||||
this.guildId = guildId;
|
this.guildId = guildId;
|
||||||
}
|
}
|
||||||
|
|
||||||
[util.inspect.custom](depth, options) {
|
[util.inspect.custom]() {
|
||||||
return `PRE [${this.pluginName}] [${this.guildId}] ${this.message}`;
|
return `PRE [${this.pluginName}] [${this.guildId}] ${this.message}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this[util.inspect.custom]();
|
return this[util.inspect.custom];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
src/data/GuildVCAlerts.ts
Normal file
54
src/data/GuildVCAlerts.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import { BaseGuildRepository } from "./BaseGuildRepository";
|
||||||
|
import { getRepository, Repository } from "typeorm";
|
||||||
|
import { VCAlert } from "./entities/VCAlert";
|
||||||
|
|
||||||
|
export class GuildVCAlerts extends BaseGuildRepository {
|
||||||
|
private allAlerts: Repository<VCAlert>;
|
||||||
|
|
||||||
|
constructor(guildId) {
|
||||||
|
super(guildId);
|
||||||
|
this.allAlerts = getRepository(VCAlert);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOutdatedAlerts(): Promise<VCAlert[]> {
|
||||||
|
return this.allAlerts
|
||||||
|
.createQueryBuilder()
|
||||||
|
.where("guild_id = :guildId", { guildId: this.guildId })
|
||||||
|
.andWhere("expires_at <= NOW()")
|
||||||
|
.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllGuildAlerts(): Promise<VCAlert[]> {
|
||||||
|
return this.allAlerts
|
||||||
|
.createQueryBuilder()
|
||||||
|
.where("guild_id = :guildId", { guildId: this.guildId })
|
||||||
|
.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAlertsByUserId(userId: string): Promise<VCAlert[]> {
|
||||||
|
return this.allAlerts.find({
|
||||||
|
where: {
|
||||||
|
guild_id: this.guildId,
|
||||||
|
user_id: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id) {
|
||||||
|
await this.allAlerts.delete({
|
||||||
|
guild_id: this.guildId,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async add(requestorId: string, userId: string, channelId: string, expiresAt: string, body: string) {
|
||||||
|
await this.allAlerts.insert({
|
||||||
|
guild_id: this.guildId,
|
||||||
|
requestor_id: requestorId,
|
||||||
|
user_id: userId,
|
||||||
|
channel_id: channelId,
|
||||||
|
expires_at: expiresAt,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
20
src/data/entities/VCAlert.ts
Normal file
20
src/data/entities/VCAlert.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { Entity, Column, PrimaryColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity("vc_alerts")
|
||||||
|
export class VCAlert {
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column() guild_id: string;
|
||||||
|
|
||||||
|
@Column() requestor_id: string;
|
||||||
|
|
||||||
|
@Column() user_id: string;
|
||||||
|
|
||||||
|
@Column() channel_id: string;
|
||||||
|
|
||||||
|
@Column() expires_at: string;
|
||||||
|
|
||||||
|
@Column() body: string;
|
||||||
|
}
|
58
src/migrations/1561391921385-AddVCAlertTable.ts
Normal file
58
src/migrations/1561391921385-AddVCAlertTable.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { MigrationInterface, QueryRunner, Table } from "typeorm";
|
||||||
|
|
||||||
|
export class AddVCAlertTable1561391921385 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "vc_alerts",
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
type: "int",
|
||||||
|
unsigned: true,
|
||||||
|
isGenerated: true,
|
||||||
|
generationStrategy: "increment",
|
||||||
|
isPrimary: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "guild_id",
|
||||||
|
type: "bigint",
|
||||||
|
unsigned: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "requestor_id",
|
||||||
|
type: "bigint",
|
||||||
|
unsigned: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "user_id",
|
||||||
|
type: "bigint",
|
||||||
|
unsigned: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "channel_id",
|
||||||
|
type: "bigint",
|
||||||
|
unsigned: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expires_at",
|
||||||
|
type: "datetime",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "body",
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indices: [
|
||||||
|
{
|
||||||
|
columnNames: ["guild_id", "user_id"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.dropTable("vc_alerts", true, false, true);
|
||||||
|
}
|
||||||
|
}
|
148
src/plugins/LocateUser.ts
Normal file
148
src/plugins/LocateUser.ts
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import { decorators as d, IPluginOptions, getInviteLink, logger } from "knub";
|
||||||
|
import { ZeppelinPlugin } from "./ZeppelinPlugin";
|
||||||
|
import humanizeDuration from "humanize-duration";
|
||||||
|
import { Message, Member, Guild, TextableChannel, VoiceChannel, Channel } from "eris";
|
||||||
|
import { GuildVCAlerts } from "../data/GuildVCAlerts";
|
||||||
|
import moment = require("moment");
|
||||||
|
import { resolveMember } from "../utils";
|
||||||
|
|
||||||
|
const ALERT_LOOP_TIME = 30 * 1000;
|
||||||
|
|
||||||
|
interface ILocatePluginConfig {
|
||||||
|
can_where: boolean;
|
||||||
|
can_alert: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LocatePlugin extends ZeppelinPlugin<ILocatePluginConfig> {
|
||||||
|
public static pluginName = "locate_user";
|
||||||
|
|
||||||
|
private alerts: GuildVCAlerts;
|
||||||
|
private outdatedAlertsTimeout;
|
||||||
|
private usersWithAlerts: string[] = [];
|
||||||
|
|
||||||
|
getDefaultOptions(): IPluginOptions<ILocatePluginConfig> {
|
||||||
|
return {
|
||||||
|
config: {
|
||||||
|
can_where: false,
|
||||||
|
can_alert: false,
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
level: ">=50",
|
||||||
|
config: {
|
||||||
|
can_where: true,
|
||||||
|
can_alert: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
this.alerts = GuildVCAlerts.getGuildInstance(this.guildId);
|
||||||
|
this.outdatedAlertsLoop();
|
||||||
|
this.fillActiveAlertsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
async outdatedAlertsLoop() {
|
||||||
|
const outdatedAlerts = await this.alerts.getOutdatedAlerts();
|
||||||
|
|
||||||
|
for (const alert of outdatedAlerts) {
|
||||||
|
await this.alerts.delete(alert.id);
|
||||||
|
await this.removeUserIDFromActiveAlerts(alert.user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.outdatedAlertsTimeout = setTimeout(() => this.outdatedAlertsLoop(), ALERT_LOOP_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fillActiveAlertsList() {
|
||||||
|
const allAlerts = await this.alerts.getAllGuildAlerts();
|
||||||
|
|
||||||
|
allAlerts.forEach(alert => {
|
||||||
|
if (!this.usersWithAlerts.includes(alert.user_id)) {
|
||||||
|
this.usersWithAlerts.push(alert.user_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.command("where", "<member:resolvedMember>", {})
|
||||||
|
@d.permission("can_where")
|
||||||
|
async whereCmd(msg: Message, args: { member: Member; time?: number; reminder?: string }) {
|
||||||
|
let member = await resolveMember(this.bot, this.guild, args.member.id);
|
||||||
|
sendWhere(this.guild, member, msg.channel, `${msg.member.mention} |`);
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.command("alert", "<member:resolvedMember> [duration:delay] [reminder:string$]", {})
|
||||||
|
@d.permission("can_alert")
|
||||||
|
async notifyRequest(msg: Message, args: { member: Member; duration?: number; reminder?: string }) {
|
||||||
|
let time = args.duration || 600000;
|
||||||
|
let alertTime = moment().add(time, "millisecond");
|
||||||
|
let body = args.reminder || "None";
|
||||||
|
|
||||||
|
this.alerts.add(msg.author.id, args.member.id, msg.channel.id, alertTime.format("YYYY-MM-DD HH:mm:ss"), body);
|
||||||
|
if (!this.usersWithAlerts.includes(args.member.id)) {
|
||||||
|
this.usersWithAlerts.push(args.member.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.channel.createMessage(
|
||||||
|
`If ${args.member.mention} joins or switches VC in the next ${humanizeDuration(time)} i will notify you`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.event("voiceChannelJoin")
|
||||||
|
async userJoinedVC(member: Member, channel: Channel) {
|
||||||
|
if (this.usersWithAlerts.includes(member.id)) {
|
||||||
|
this.sendAlerts(member.id);
|
||||||
|
await this.removeUserIDFromActiveAlerts(member.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.event("voiceChannelSwitch")
|
||||||
|
async userSwitchedVC(member: Member, newChannel: Channel, oldChannel: Channel) {
|
||||||
|
if (this.usersWithAlerts.includes(member.id)) {
|
||||||
|
this.sendAlerts(member.id);
|
||||||
|
await this.removeUserIDFromActiveAlerts(member.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendAlerts(userid: string) {
|
||||||
|
const triggeredAlerts = await this.alerts.getAlertsByUserId(userid);
|
||||||
|
const member = await resolveMember(this.bot, this.guild, userid);
|
||||||
|
|
||||||
|
triggeredAlerts.forEach(alert => {
|
||||||
|
let prepend = `<@!${alert.requestor_id}>, an alert requested by you has triggered!\nReminder: \`${alert.body}\`\n`;
|
||||||
|
sendWhere(this.guild, member, <TextableChannel>this.bot.getChannel(alert.channel_id), prepend);
|
||||||
|
this.alerts.delete(alert.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeUserIDFromActiveAlerts(userid: string) {
|
||||||
|
const index = this.usersWithAlerts.indexOf(userid);
|
||||||
|
if (index > -1) {
|
||||||
|
this.usersWithAlerts.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendWhere(guild: Guild, member: Member, channel: TextableChannel, prepend: string) {
|
||||||
|
let voice = await (<VoiceChannel>guild.channels.get(member.voiceState.channelID));
|
||||||
|
|
||||||
|
if (voice == null) {
|
||||||
|
channel.createMessage(prepend + "That user is not in a channel");
|
||||||
|
} else {
|
||||||
|
let invite = await createInvite(voice);
|
||||||
|
channel.createMessage(
|
||||||
|
prepend + ` ${member.mention} is in the following channel: ${voice.name} https://${getInviteLink(invite)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createInvite(vc: VoiceChannel) {
|
||||||
|
let existingInvites = await vc.getInvites();
|
||||||
|
|
||||||
|
if (existingInvites.length !== 0) {
|
||||||
|
return existingInvites[0];
|
||||||
|
} else {
|
||||||
|
return vc.createInvite(undefined);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import { BotControlPlugin } from "./BotControl";
|
||||||
import { UsernameSaver } from "./UsernameSaver";
|
import { UsernameSaver } from "./UsernameSaver";
|
||||||
import { CustomEventsPlugin } from "./CustomEvents";
|
import { CustomEventsPlugin } from "./CustomEvents";
|
||||||
import { GuildInfoSaverPlugin } from "./GuildInfoSaver";
|
import { GuildInfoSaverPlugin } from "./GuildInfoSaver";
|
||||||
|
import { LocatePlugin } from "./LocateUser";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugins available to be loaded for individual guilds
|
* Plugins available to be loaded for individual guilds
|
||||||
|
@ -49,6 +50,7 @@ export const availablePlugins = [
|
||||||
WelcomeMessagePlugin,
|
WelcomeMessagePlugin,
|
||||||
CustomEventsPlugin,
|
CustomEventsPlugin,
|
||||||
GuildInfoSaverPlugin,
|
GuildInfoSaverPlugin,
|
||||||
|
LocatePlugin,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue