Add time_and_date plugin. Use it for timezones and date formats around the bot.
This commit is contained in:
parent
cffb0dbd6b
commit
4ae8cf85a3
67 changed files with 543 additions and 177 deletions
|
@ -17,6 +17,7 @@ import { createTypeHelper } from "knub-command-manager";
|
|||
import { getChannelIdFromMessageId } from "./data/getChannelIdFromMessageId";
|
||||
import { MessageTarget, resolveMessageTarget } from "./utils/resolveMessageTarget";
|
||||
import { inputPatternToRegExp } from "./validatorUtils";
|
||||
import { isValidTimezone } from "./utils/isValidTimezone";
|
||||
|
||||
export const commandTypes = {
|
||||
...baseTypeConverters,
|
||||
|
@ -93,6 +94,14 @@ export const commandTypes = {
|
|||
throw new TypeConversionError(`Could not parse RegExp: \`${disableInlineCode(e.message)}\``);
|
||||
}
|
||||
},
|
||||
|
||||
timezone(value: string) {
|
||||
if (!isValidTimezone(value)) {
|
||||
throw new TypeConversionError(`Invalid timezone: ${disableInlineCode(value)}`);
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
};
|
||||
|
||||
export const commandTypeHelpers = {
|
||||
|
@ -105,4 +114,5 @@ export const commandTypeHelpers = {
|
|||
messageTarget: createTypeHelper<Promise<MessageTarget>>(commandTypes.messageTarget),
|
||||
anyId: createTypeHelper<Promise<string>>(commandTypes.anyId),
|
||||
regex: createTypeHelper<RegExp>(commandTypes.regex),
|
||||
timezone: createTypeHelper<string>(commandTypes.timezone),
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ import crypto from "crypto";
|
|||
import moment from "moment-timezone";
|
||||
// tslint:disable-next-line:no-submodule-imports
|
||||
import uuidv4 from "uuid/v4";
|
||||
import { DBDateFormat } from "../utils/dateFormats";
|
||||
import { DBDateFormat } from "../utils";
|
||||
|
||||
export class ApiLogins extends BaseRepository {
|
||||
private apiLogins: Repository<ApiLogin>;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ApiUserInfo as ApiUserInfoEntity, ApiUserInfoData } from "./entities/Ap
|
|||
import { BaseRepository } from "./BaseRepository";
|
||||
import { connection } from "./db";
|
||||
import moment from "moment-timezone";
|
||||
import { DBDateFormat } from "../utils/dateFormats";
|
||||
import { DBDateFormat } from "../utils";
|
||||
|
||||
export class ApiUserInfo extends BaseRepository {
|
||||
private apiUserInfo: Repository<ApiUserInfoEntity>;
|
||||
|
|
|
@ -2,11 +2,10 @@ import { Case } from "./entities/Case";
|
|||
import { CaseNote } from "./entities/CaseNote";
|
||||
import { BaseGuildRepository } from "./BaseGuildRepository";
|
||||
import { getRepository, In, Repository } from "typeorm";
|
||||
import { disableLinkPreviews } from "../utils";
|
||||
import { DBDateFormat, disableLinkPreviews } from "../utils";
|
||||
import { CaseTypes } from "./CaseTypes";
|
||||
import moment = require("moment-timezone");
|
||||
import { connection } from "./db";
|
||||
import { DBDateFormat } from "../utils/dateFormats";
|
||||
|
||||
const CASE_SUMMARY_REASON_MAX_LENGTH = 300;
|
||||
|
||||
|
|
48
backend/src/data/GuildMemberTimezones.ts
Normal file
48
backend/src/data/GuildMemberTimezones.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { BaseGuildRepository } from "./BaseGuildRepository";
|
||||
import { MemberTimezone } from "./entities/MemberTimezone";
|
||||
import { getRepository, Repository } from "typeorm/index";
|
||||
import { connection } from "./db";
|
||||
|
||||
export class GuildMemberTimezones extends BaseGuildRepository {
|
||||
protected memberTimezones: Repository<MemberTimezone>;
|
||||
|
||||
constructor(guildId: string) {
|
||||
super(guildId);
|
||||
this.memberTimezones = getRepository(MemberTimezone);
|
||||
}
|
||||
|
||||
get(memberId: string) {
|
||||
return this.memberTimezones.findOne({
|
||||
guild_id: this.guildId,
|
||||
member_id: memberId,
|
||||
});
|
||||
}
|
||||
|
||||
async set(memberId, timezone: string) {
|
||||
await connection.transaction(async entityManager => {
|
||||
const repo = entityManager.getRepository(MemberTimezone);
|
||||
const existingRow = await repo.findOne({
|
||||
guild_id: this.guildId,
|
||||
member_id: memberId,
|
||||
});
|
||||
|
||||
if (existingRow) {
|
||||
await repo.update(
|
||||
{
|
||||
guild_id: this.guildId,
|
||||
member_id: memberId,
|
||||
},
|
||||
{
|
||||
timezone,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await repo.insert({
|
||||
guild_id: this.guildId,
|
||||
member_id: memberId,
|
||||
timezone,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import { connection } from "../db";
|
|||
import { getRepository, In } from "typeorm";
|
||||
import { Config } from "../entities/Config";
|
||||
import moment from "moment-timezone";
|
||||
import { DBDateFormat } from "../../utils/dateFormats";
|
||||
import { DBDateFormat } from "../../utils";
|
||||
|
||||
const CLEAN_PER_LOOP = 50;
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { DAYS, MINUTES } from "../../utils";
|
||||
import { DAYS, DBDateFormat, MINUTES } from "../../utils";
|
||||
import { getRepository, In } from "typeorm";
|
||||
import { SavedMessage } from "../entities/SavedMessage";
|
||||
import moment from "moment-timezone";
|
||||
import { connection } from "../db";
|
||||
import { DBDateFormat } from "../../utils/dateFormats";
|
||||
|
||||
/**
|
||||
* How long message edits, deletions, etc. will include the original message content.
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { getRepository, In } from "typeorm";
|
||||
import moment from "moment-timezone";
|
||||
import { NicknameHistoryEntry } from "../entities/NicknameHistoryEntry";
|
||||
import { DAYS } from "../../utils";
|
||||
import { DAYS, DBDateFormat } from "../../utils";
|
||||
import { connection } from "../db";
|
||||
import { DBDateFormat } from "../../utils/dateFormats";
|
||||
|
||||
export const NICKNAME_RETENTION_PERIOD = 30 * DAYS;
|
||||
const CLEAN_PER_LOOP = 500;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { getRepository, In } from "typeorm";
|
||||
import moment from "moment-timezone";
|
||||
import { UsernameHistoryEntry } from "../entities/UsernameHistoryEntry";
|
||||
import { DAYS } from "../../utils";
|
||||
import { DAYS, DBDateFormat } from "../../utils";
|
||||
import { connection } from "../db";
|
||||
import { DBDateFormat } from "../../utils/dateFormats";
|
||||
|
||||
export const USERNAME_RETENTION_PERIOD = 30 * DAYS;
|
||||
const CLEAN_PER_LOOP = 500;
|
||||
|
|
14
backend/src/data/entities/MemberTimezone.ts
Normal file
14
backend/src/data/entities/MemberTimezone.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { Column, Entity, PrimaryColumn } from "typeorm";
|
||||
|
||||
@Entity("member_timezones")
|
||||
export class MemberTimezone {
|
||||
@Column()
|
||||
@PrimaryColumn()
|
||||
guild_id: string;
|
||||
|
||||
@Column()
|
||||
@PrimaryColumn()
|
||||
member_id: string;
|
||||
|
||||
@Column() timezone: string;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
import { Table } from "typeorm/index";
|
||||
|
||||
export class CreateMemberTimezonesTable1597109357201 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: "member_timezones",
|
||||
columns: [
|
||||
{
|
||||
name: "guild_id",
|
||||
type: "bigint",
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
name: "member_id",
|
||||
type: "bigint",
|
||||
isPrimary: true,
|
||||
},
|
||||
{
|
||||
name: "timezone",
|
||||
type: "varchar",
|
||||
length: "255",
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.dropTable("member_timezones");
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import { TZeppelinKnub } from "./types";
|
|||
import { ExtendedMatchParams } from "knub/dist/config/PluginConfigManager"; // TODO: Export from Knub index
|
||||
import * as t from "io-ts";
|
||||
import { PluginOverrideCriteria } from "knub/dist/config/configTypes";
|
||||
import { Tail } from "./utils/typeUtils";
|
||||
|
||||
const { getMemberLevel } = helpers;
|
||||
|
||||
|
@ -168,3 +169,16 @@ export function isOwner(pluginData: PluginData<any>, userId: string) {
|
|||
export const isOwnerPreFilter = (_, context: CommandContext<any>) => {
|
||||
return isOwner(context.pluginData, context.message.author.id);
|
||||
};
|
||||
|
||||
type AnyFn = (...args: any[]) => any;
|
||||
|
||||
/**
|
||||
* Creates a public plugin function out of a function with pluginData as the first parameter
|
||||
*/
|
||||
export function mapToPublicFn<T extends AnyFn>(inputFn: T) {
|
||||
return pluginData => {
|
||||
return (...args: Tail<Parameters<typeof inputFn>>): ReturnType<typeof inputFn> => {
|
||||
return inputFn(pluginData, ...args);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { GuildLogs } from "src/data/GuildLogs";
|
|||
import { onMessageCreate } from "./util/onMessageCreate";
|
||||
import { onMessageDelete } from "./util/onMessageDelete";
|
||||
import { onMessageDeleteBulk } from "./util/onMessageDeleteBulk";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const defaultOptions: PluginOptions<AutoDeletePluginType> = {
|
||||
config: {
|
||||
|
@ -22,6 +23,7 @@ export const AutoDeletePlugin = zeppelinPlugin<AutoDeletePluginType>()("auto_del
|
|||
configurationGuide: "Maximum deletion delay is currently 5 minutes",
|
||||
},
|
||||
|
||||
dependencies: [TimeAndDatePlugin],
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
|
|
|
@ -5,13 +5,14 @@ import { LogType } from "src/data/LogType";
|
|||
import { stripObjectToScalars, resolveUser } from "src/utils";
|
||||
import { logger } from "src/logger";
|
||||
import { scheduleNextDeletion } from "./scheduleNextDeletion";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export async function deleteNextItem(pluginData: PluginData<AutoDeletePluginType>) {
|
||||
const [itemToDelete] = pluginData.state.deletionQueue.splice(0, 1);
|
||||
if (!itemToDelete) return;
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
pluginData.state.guildLogs.ignoreLog(LogType.MESSAGE_DELETE, itemToDelete.message.id);
|
||||
pluginData.client.deleteMessage(itemToDelete.message.channel_id, itemToDelete.message.id).catch(logger.warn);
|
||||
|
||||
|
@ -19,9 +20,9 @@ export async function deleteNextItem(pluginData: PluginData<AutoDeletePluginType
|
|||
|
||||
const user = await resolveUser(pluginData.client, itemToDelete.message.user_id);
|
||||
const channel = pluginData.guild.channels.get(itemToDelete.message.channel_id);
|
||||
const messageDate = inGuildTz(pluginData, moment.utc(itemToDelete.message.data.timestamp, "x")).format(
|
||||
getDateFormat(pluginData, "pretty_datetime"),
|
||||
);
|
||||
const messageDate = timeAndDate
|
||||
.inGuildTz(moment.utc(itemToDelete.message.data.timestamp, "x"))
|
||||
.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
|
||||
pluginData.state.guildLogs.log(LogType.MESSAGE_DELETE_AUTO, {
|
||||
message: itemToDelete.message,
|
||||
|
|
|
@ -12,6 +12,8 @@ import { getCaseTypeAmountForUserId } from "./functions/getCaseTypeAmountForUser
|
|||
import { getCaseEmbed } from "./functions/getCaseEmbed";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { getCaseSummary } from "./functions/getCaseSummary";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
import { mapToPublicFn } from "../../pluginUtils";
|
||||
|
||||
const defaultOptions = {
|
||||
config: {
|
||||
|
@ -33,6 +35,7 @@ export const CasesPlugin = zeppelinPlugin<CasesPluginType>()("cases", {
|
|||
`),
|
||||
},
|
||||
|
||||
dependencies: [TimeAndDatePlugin],
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
|
@ -61,17 +64,8 @@ export const CasesPlugin = zeppelinPlugin<CasesPluginType>()("cases", {
|
|||
};
|
||||
},
|
||||
|
||||
getCaseEmbed(pluginData) {
|
||||
return (caseOrCaseId: Case | number) => {
|
||||
return getCaseEmbed(pluginData, caseOrCaseId);
|
||||
};
|
||||
},
|
||||
|
||||
getCaseSummary(pluginData) {
|
||||
return (caseOrCaseId: Case | number, withLinks = false) => {
|
||||
return getCaseSummary(pluginData, caseOrCaseId, withLinks);
|
||||
};
|
||||
},
|
||||
getCaseEmbed: mapToPublicFn(getCaseEmbed),
|
||||
getCaseSummary: mapToPublicFn(getCaseSummary),
|
||||
},
|
||||
|
||||
onLoad(pluginData) {
|
||||
|
|
|
@ -6,17 +6,19 @@ import { PluginData, helpers } from "knub";
|
|||
import { CasesPluginType } from "../types";
|
||||
import { resolveCaseId } from "./resolveCaseId";
|
||||
import { chunkLines, chunkMessageLines, emptyEmbedValue, messageLink } from "../../../utils";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { getCaseColor } from "./getCaseColor";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export async function getCaseEmbed(
|
||||
pluginData: PluginData<CasesPluginType>,
|
||||
caseOrCaseId: Case | number,
|
||||
requestMemberId?: string,
|
||||
): Promise<AdvancedMessageContent> {
|
||||
const theCase = await pluginData.state.cases.with("notes").find(resolveCaseId(caseOrCaseId));
|
||||
if (!theCase) return null;
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
const createdAt = moment.utc(theCase.created_at);
|
||||
const actionTypeStr = CaseTypes[theCase.type].toUpperCase();
|
||||
|
||||
|
@ -26,10 +28,14 @@ export async function getCaseEmbed(
|
|||
let modName = theCase.mod_name;
|
||||
if (theCase.mod_id) modName += `\n<@!${theCase.mod_id}>`;
|
||||
|
||||
const createdAtWithTz = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, createdAt)
|
||||
: timeAndDate.inGuildTz(createdAt);
|
||||
|
||||
const embed: any = {
|
||||
title: `${actionTypeStr} - Case #${theCase.case_number}`,
|
||||
footer: {
|
||||
text: `Case created on ${inGuildTz(pluginData, createdAt).format(getDateFormat(pluginData, "pretty_datetime"))}`,
|
||||
text: `Case created on ${createdAtWithTz.format(timeAndDate.getDateFormat("pretty_datetime"))}`,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
|
@ -56,7 +62,7 @@ export async function getCaseEmbed(
|
|||
embed.color = getCaseColor(pluginData, theCase.type);
|
||||
|
||||
if (theCase.notes.length) {
|
||||
theCase.notes.forEach((note: any) => {
|
||||
for (const note of theCase.notes) {
|
||||
const noteDate = moment.utc(note.created_at);
|
||||
let noteBody = note.body.trim();
|
||||
if (noteBody === "") {
|
||||
|
@ -67,7 +73,10 @@ export async function getCaseEmbed(
|
|||
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
if (i === 0) {
|
||||
const prettyNoteDate = inGuildTz(pluginData, noteDate).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const noteDateWithTz = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, noteDate)
|
||||
: timeAndDate.inGuildTz(noteDate);
|
||||
const prettyNoteDate = noteDateWithTz.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
embed.fields.push({
|
||||
name: `${note.mod_name} at ${prettyNoteDate}:`,
|
||||
value: chunks[i],
|
||||
|
@ -79,7 +88,7 @@ export async function getCaseEmbed(
|
|||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
embed.fields.push({
|
||||
name: "!!! THIS CASE HAS NO NOTES !!!",
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
import { PluginData } from "knub";
|
||||
import { CasesPluginType } from "../types";
|
||||
import { convertDelayStringToMS, DAYS, disableLinkPreviews, emptyEmbedValue, messageLink } from "../../../utils";
|
||||
import { DBDateFormat, getDateFormat } from "../../../utils/dateFormats";
|
||||
import {
|
||||
convertDelayStringToMS,
|
||||
DAYS,
|
||||
DBDateFormat,
|
||||
disableLinkPreviews,
|
||||
emptyEmbedValue,
|
||||
messageLink,
|
||||
} from "../../../utils";
|
||||
import { CaseTypes, CaseTypeToName } from "../../../data/CaseTypes";
|
||||
import moment from "moment-timezone";
|
||||
import { Case } from "../../../data/entities/Case";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { humanizeDurationShort } from "../../../humanizeDurationShort";
|
||||
import { caseAbbreviations } from "../caseAbbreviations";
|
||||
import { getCaseIcon } from "./getCaseIcon";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const CASE_SUMMARY_REASON_MAX_LENGTH = 300;
|
||||
const INCLUDE_MORE_NOTES_THRESHOLD = 20;
|
||||
|
@ -21,8 +27,10 @@ export async function getCaseSummary(
|
|||
pluginData: PluginData<CasesPluginType>,
|
||||
caseOrCaseId: Case | number,
|
||||
withLinks = false,
|
||||
requestMemberId?: string,
|
||||
) {
|
||||
const config = pluginData.config.get();
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
const caseId = caseOrCaseId instanceof Case ? caseOrCaseId.id : caseOrCaseId;
|
||||
const theCase = await pluginData.state.cases.with("notes").find(caseId);
|
||||
|
@ -50,9 +58,12 @@ export async function getCaseSummary(
|
|||
const timestamp = moment.utc(theCase.created_at, DBDateFormat);
|
||||
const relativeTimeCutoff = convertDelayStringToMS(config.relative_time_cutoff);
|
||||
const useRelativeTime = config.show_relative_times && Date.now() - timestamp.valueOf() < relativeTimeCutoff;
|
||||
const timestampWithTz = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, timestamp)
|
||||
: timeAndDate.inGuildTz(timestamp);
|
||||
const prettyTimestamp = useRelativeTime
|
||||
? moment.utc().to(timestamp)
|
||||
: inGuildTz(pluginData, timestamp).format(getDateFormat(pluginData, "date"));
|
||||
: timestampWithTz.format(timeAndDate.getDateFormat("date"));
|
||||
|
||||
const icon = getCaseIcon(pluginData, theCase.type);
|
||||
|
||||
|
|
|
@ -2,9 +2,12 @@ import { zeppelinPlugin } from "../ZeppelinPluginBlueprint";
|
|||
import { ChannelArchiverPluginType } from "./types";
|
||||
import { ArchiveChannelCmd } from "./commands/ArchiveChannelCmd";
|
||||
import * as t from "io-ts";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export const ChannelArchiverPlugin = zeppelinPlugin<ChannelArchiverPluginType>()("channel_archiver", {
|
||||
showInDocs: false,
|
||||
|
||||
dependencies: [TimeAndDatePlugin],
|
||||
configSchema: t.type({}),
|
||||
|
||||
// prettier-ignore
|
||||
|
|
|
@ -4,8 +4,7 @@ import { isOwner, sendErrorMessage } from "src/pluginUtils";
|
|||
import { confirm, SECONDS, noop } from "src/utils";
|
||||
import moment from "moment-timezone";
|
||||
import { rehostAttachment } from "../rehostAttachment";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const MAX_ARCHIVED_MESSAGES = 5000;
|
||||
const MAX_MESSAGES_PER_FETCH = 100;
|
||||
|
@ -98,7 +97,8 @@ export const ArchiveChannelCmd = channelArchiverCmd({
|
|||
|
||||
archiveLines.reverse();
|
||||
|
||||
const nowTs = inGuildTz(pluginData).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
const nowTs = timeAndDate.inGuildTz().format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
|
||||
let result = `Archived ${archiveLines.length} messages from #${args.channel.name} at ${nowTs}`;
|
||||
result += `\n\n${archiveLines.join("\n")}\n`;
|
||||
|
|
|
@ -22,6 +22,7 @@ import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
|
|||
import { disableCodeBlocks } from "../../utils";
|
||||
import { logger } from "../../logger";
|
||||
import { CasesPlugin } from "../Cases/CasesPlugin";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const defaultOptions: PluginOptions<LogsPluginType> = {
|
||||
config: {
|
||||
|
@ -49,7 +50,7 @@ export const LogsPlugin = zeppelinPlugin<LogsPluginType>()("logs", {
|
|||
prettyName: "Logs",
|
||||
},
|
||||
|
||||
dependencies: [CasesPlugin],
|
||||
dependencies: [TimeAndDatePlugin, CasesPlugin],
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import { SavedMessage } from "src/data/entities/SavedMessage";
|
|||
import { renderTemplate, TemplateParseError } from "src/templateFormatter";
|
||||
import { logger } from "src/logger";
|
||||
import moment from "moment-timezone";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export async function getLogMessage(
|
||||
pluginData: PluginData<LogsPluginType>,
|
||||
|
@ -88,7 +88,10 @@ export async function getLogMessage(
|
|||
|
||||
const timestampFormat = config.format.timestamp;
|
||||
if (timestampFormat) {
|
||||
const timestamp = inGuildTz(pluginData).format(timestampFormat);
|
||||
const timestamp = pluginData
|
||||
.getPlugin(TimeAndDatePlugin)
|
||||
.inGuildTz()
|
||||
.format(timestampFormat);
|
||||
formatted = `\`[${timestamp}]\` ${formatted}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { LogType } from "src/data/LogType";
|
|||
import moment from "moment-timezone";
|
||||
import { PluginData } from "knub";
|
||||
import { LogsPluginType } from "../types";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export async function onMessageDelete(pluginData: PluginData<LogsPluginType>, savedMessage: SavedMessage) {
|
||||
const user = await resolveUser(pluginData.client, savedMessage.user_id);
|
||||
|
@ -24,9 +24,10 @@ export async function onMessageDelete(pluginData: PluginData<LogsPluginType>, sa
|
|||
{
|
||||
user: stripObjectToScalars(user),
|
||||
channel: stripObjectToScalars(channel),
|
||||
messageDate: inGuildTz(pluginData, moment.utc(savedMessage.data.timestamp, "x")).format(
|
||||
pluginData.config.get().format.timestamp,
|
||||
),
|
||||
messageDate: pluginData
|
||||
.getPlugin(TimeAndDatePlugin)
|
||||
.inGuildTz(moment.utc(savedMessage.data.timestamp, "x"))
|
||||
.format(pluginData.config.get().format.timestamp),
|
||||
message: savedMessage,
|
||||
},
|
||||
savedMessage.id,
|
||||
|
|
|
@ -35,6 +35,7 @@ import { banUserId } from "./functions/banUserId";
|
|||
import { MassmuteCmd } from "./commands/MassmuteCmd";
|
||||
import { trimPluginDescription } from "../../utils";
|
||||
import { DeleteCaseCmd } from "./commands/DeleteCaseCmd";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const defaultOptions = {
|
||||
config: {
|
||||
|
@ -103,11 +104,10 @@ export const ModActionsPlugin = zeppelinPlugin<ModActionsPluginType>()("mod_acti
|
|||
`),
|
||||
},
|
||||
|
||||
dependencies: [TimeAndDatePlugin, CasesPlugin, MutesPlugin],
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
dependencies: [CasesPlugin, MutesPlugin],
|
||||
|
||||
events: [
|
||||
CreateBanCaseOnManualBanEvt,
|
||||
CreateUnbanCaseOnManualUnbanEvt,
|
||||
|
|
|
@ -23,7 +23,7 @@ export const CaseCmd = modActionsCommand({
|
|||
}
|
||||
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const embed = await casesPlugin.getCaseEmbed(theCase.id);
|
||||
const embed = await casesPlugin.getCaseEmbed(theCase.id, msg.author.id);
|
||||
msg.channel.createMessage(embed);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -36,7 +36,7 @@ export const CasesModCmd = modActionsCommand({
|
|||
sendErrorMessage(pluginData, msg.channel, `No cases by **${modName}**`);
|
||||
} else {
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const lines = await asyncMap(recentCases, c => casesPlugin.getCaseSummary(c, true));
|
||||
const lines = await asyncMap(recentCases, c => casesPlugin.getCaseSummary(c, true, msg.author.id));
|
||||
const prefix = getGuildPrefix(pluginData);
|
||||
const embed: EmbedOptions = {
|
||||
author: {
|
||||
|
|
|
@ -67,7 +67,7 @@ export const CasesUserCmd = modActionsCommand({
|
|||
} else {
|
||||
// Compact view (= regular message with a preview of each case)
|
||||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const lines = await asyncMap(casesToDisplay, c => casesPlugin.getCaseSummary(c, true));
|
||||
const lines = await asyncMap(casesToDisplay, c => casesPlugin.getCaseSummary(c, true, msg.author.id));
|
||||
|
||||
const prefix = getGuildPrefix(pluginData);
|
||||
const linesPerChunk = 15;
|
||||
|
|
|
@ -8,8 +8,7 @@ import { SECONDS, stripObjectToScalars, trimLines } from "../../../utils";
|
|||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import moment from "moment-timezone";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export const DeleteCaseCmd = modActionsCommand({
|
||||
trigger: ["delete_case", "deletecase"],
|
||||
|
@ -54,7 +53,9 @@ export const DeleteCaseCmd = modActionsCommand({
|
|||
}
|
||||
|
||||
const deletedByName = `${message.author.username}#${message.author.discriminator}`;
|
||||
const deletedAt = inGuildTz(pluginData).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
const deletedAt = timeAndDate.inGuildTz().format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
|
||||
await pluginData.state.cases.softDelete(
|
||||
theCase.id,
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { command } from "knub";
|
||||
import { IMuteWithDetails, MutesPluginType } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { isFullMessage, MINUTES, noop, resolveMember } from "../../../utils";
|
||||
import { DBDateFormat, isFullMessage, MINUTES, noop, resolveMember } from "../../../utils";
|
||||
import moment from "moment-timezone";
|
||||
import { humanizeDurationShort } from "../../../humanizeDurationShort";
|
||||
import { getBaseUrl } from "../../../pluginUtils";
|
||||
import { DBDateFormat } from "../../../utils/dateFormats";
|
||||
|
||||
export const MutesCmd = command<MutesPluginType>()({
|
||||
trigger: "mutes",
|
||||
|
|
|
@ -12,6 +12,7 @@ import { ScheduledPostsShowCmd } from "./commands/ScheduledPostsShowCmd";
|
|||
import { ScheduledPostsListCmd } from "./commands/ScheduledPostsListCmd";
|
||||
import { ScheduledPostsDeleteCmd } from "./commands/SchedluedPostsDeleteCmd";
|
||||
import { scheduledPostLoop } from "./util/scheduledPostLoop";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const defaultOptions: PluginOptions<PostPluginType> = {
|
||||
config: {
|
||||
|
@ -33,6 +34,7 @@ export const PostPlugin = zeppelinPlugin<PostPluginType>()("post", {
|
|||
prettyName: "Post",
|
||||
},
|
||||
|
||||
dependencies: [TimeAndDatePlugin],
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import { postCmd } from "../types";
|
||||
import { trimLines, sorter, disableCodeBlocks, deactivateMentions, createChunkedMessage } from "src/utils";
|
||||
import {
|
||||
trimLines,
|
||||
sorter,
|
||||
disableCodeBlocks,
|
||||
deactivateMentions,
|
||||
createChunkedMessage,
|
||||
DBDateFormat,
|
||||
} from "src/utils";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import moment from "moment-timezone";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { DBDateFormat, getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const SCHEDULED_POST_PREVIEW_TEXT_LENGTH = 50;
|
||||
|
||||
|
@ -31,9 +37,10 @@ export const ScheduledPostsListCmd = postCmd({
|
|||
.replace(/\s+/g, " ")
|
||||
.slice(0, SCHEDULED_POST_PREVIEW_TEXT_LENGTH);
|
||||
|
||||
const prettyPostAt = inGuildTz(pluginData, moment.utc(p.post_at, DBDateFormat)).format(
|
||||
getDateFormat(pluginData, "pretty_datetime"),
|
||||
);
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
const prettyPostAt = timeAndDate
|
||||
.inGuildTz(moment.utc(p.post_at, DBDateFormat))
|
||||
.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const parts = [`\`#${i++}\` \`[${prettyPostAt}]\` ${previewText}${isTruncated ? "..." : ""}`];
|
||||
if (p.attachments.length) parts.push("*(with attachment)*");
|
||||
if (p.content.embed) parts.push("*(embed)*");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Message, Channel, TextChannel } from "eris";
|
||||
import { StrictMessageContent, errorMessage, stripObjectToScalars, MINUTES } from "src/utils";
|
||||
import { StrictMessageContent, errorMessage, stripObjectToScalars, MINUTES, DBDateFormat } from "src/utils";
|
||||
import moment from "moment-timezone";
|
||||
import { LogType } from "src/data/LogType";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
|
@ -8,7 +8,7 @@ import { PluginData } from "knub";
|
|||
import { PostPluginType } from "../types";
|
||||
import { parseScheduleTime } from "./parseScheduleTime";
|
||||
import { postMessage } from "./postMessage";
|
||||
import { DBDateFormat, getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const MIN_REPEAT_TIME = 5 * MINUTES;
|
||||
const MAX_REPEAT_TIME = Math.pow(2, 32);
|
||||
|
@ -54,7 +54,7 @@ export async function actualPostCmd(
|
|||
let postAt;
|
||||
if (opts.schedule) {
|
||||
// Schedule the post to be posted later
|
||||
postAt = parseScheduleTime(pluginData, opts.schedule);
|
||||
postAt = await parseScheduleTime(pluginData, msg.author.id, opts.schedule);
|
||||
if (!postAt) {
|
||||
return sendErrorMessage(pluginData, msg.channel, "Invalid schedule time");
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ export async function actualPostCmd(
|
|||
let repeatDetailsStr: string = null;
|
||||
|
||||
if (opts["repeat-until"]) {
|
||||
repeatUntil = parseScheduleTime(pluginData, opts["repeat-until"]);
|
||||
repeatUntil = await parseScheduleTime(pluginData, msg.author.id, opts["repeat-until"]);
|
||||
|
||||
// Invalid time
|
||||
if (!repeatUntil) {
|
||||
|
@ -109,6 +109,8 @@ export async function actualPostCmd(
|
|||
: `every ${humanizeDuration(opts.repeat)}, ${repeatTimes} times in total`;
|
||||
}
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
// Save schedule/repeat information in DB
|
||||
if (postAt) {
|
||||
if (postAt < moment.utc()) {
|
||||
|
@ -140,9 +142,9 @@ export async function actualPostCmd(
|
|||
pluginData.state.logs.log(LogType.SCHEDULED_REPEATED_MESSAGE, {
|
||||
author: stripObjectToScalars(msg.author),
|
||||
channel: stripObjectToScalars(targetChannel),
|
||||
datetime: postAt.format(getDateFormat(pluginData, "pretty_datetime")),
|
||||
date: postAt.format(getDateFormat(pluginData, "date")),
|
||||
time: postAt.format(getDateFormat(pluginData, "time")),
|
||||
datetime: postAt.format(timeAndDate.getDateFormat("pretty_datetime")),
|
||||
date: postAt.format(timeAndDate.getDateFormat("date")),
|
||||
time: postAt.format(timeAndDate.getDateFormat("time")),
|
||||
repeatInterval: humanizeDuration(opts.repeat),
|
||||
repeatDetails: repeatDetailsStr,
|
||||
});
|
||||
|
@ -150,9 +152,9 @@ export async function actualPostCmd(
|
|||
pluginData.state.logs.log(LogType.SCHEDULED_MESSAGE, {
|
||||
author: stripObjectToScalars(msg.author),
|
||||
channel: stripObjectToScalars(targetChannel),
|
||||
datetime: postAt.format(getDateFormat(pluginData, "pretty_datetime")),
|
||||
date: postAt.format(getDateFormat(pluginData, "date")),
|
||||
time: postAt.format(getDateFormat(pluginData, "time")),
|
||||
datetime: postAt.format(timeAndDate.getDateFormat("pretty_datetime")),
|
||||
date: postAt.format(timeAndDate.getDateFormat("date")),
|
||||
time: postAt.format(timeAndDate.getDateFormat("time")),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -166,9 +168,9 @@ export async function actualPostCmd(
|
|||
pluginData.state.logs.log(LogType.REPEATED_MESSAGE, {
|
||||
author: stripObjectToScalars(msg.author),
|
||||
channel: stripObjectToScalars(targetChannel),
|
||||
datetime: postAt.format(getDateFormat(pluginData, "pretty_datetime")),
|
||||
date: postAt.format(getDateFormat(pluginData, "date")),
|
||||
time: postAt.format(getDateFormat(pluginData, "time")),
|
||||
datetime: postAt.format(timeAndDate.getDateFormat("pretty_datetime")),
|
||||
date: postAt.format(timeAndDate.getDateFormat("date")),
|
||||
time: postAt.format(timeAndDate.getDateFormat("time")),
|
||||
repeatInterval: humanizeDuration(opts.repeat),
|
||||
repeatDetails: repeatDetailsStr,
|
||||
});
|
||||
|
@ -177,7 +179,7 @@ export async function actualPostCmd(
|
|||
// Bot reply schenanigans
|
||||
let successMessage = opts.schedule
|
||||
? `Message scheduled to be posted in <#${targetChannel.id}> on ${postAt.format(
|
||||
getDateFormat(pluginData, "pretty_datetime"),
|
||||
timeAndDate.getDateFormat("pretty_datetime"),
|
||||
)}`
|
||||
: `Message posted in <#${targetChannel.id}>`;
|
||||
|
||||
|
@ -185,7 +187,7 @@ export async function actualPostCmd(
|
|||
successMessage += `. Message will be automatically reposted every ${humanizeDuration(opts.repeat)}`;
|
||||
|
||||
if (repeatUntil) {
|
||||
successMessage += ` until ${repeatUntil.format(getDateFormat(pluginData, "pretty_datetime"))}`;
|
||||
successMessage += ` until ${repeatUntil.format(timeAndDate.getDateFormat("pretty_datetime"))}`;
|
||||
} else if (repeatTimes) {
|
||||
successMessage += `, ${repeatTimes} times in total`;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import moment, { Moment } from "moment-timezone";
|
||||
import { convertDelayStringToMS } from "src/utils";
|
||||
import { PluginData } from "knub";
|
||||
import { getGuildTz } from "../../../utils/timezones";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
// TODO: Extract out of the Post plugin, use everywhere with a date input
|
||||
export function parseScheduleTime(pluginData: PluginData<any>, str: string): Moment {
|
||||
const tz = getGuildTz(pluginData);
|
||||
export async function parseScheduleTime(pluginData: PluginData<any>, memberId: string, str: string): Promise<Moment> {
|
||||
const tz = await pluginData.getPlugin(TimeAndDatePlugin).getMemberTz(memberId);
|
||||
|
||||
const dt1 = moment.tz(str, "YYYY-MM-DD HH:mm:ss", tz);
|
||||
if (dt1 && dt1.isValid()) return dt1;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { PluginData } from "knub";
|
||||
import { PostPluginType } from "../types";
|
||||
import { logger } from "src/logger";
|
||||
import { stripObjectToScalars, SECONDS } from "src/utils";
|
||||
import { stripObjectToScalars, SECONDS, DBDateFormat } from "src/utils";
|
||||
import { LogType } from "src/data/LogType";
|
||||
import moment from "moment-timezone";
|
||||
import { TextChannel, User } from "eris";
|
||||
import { postMessage } from "./postMessage";
|
||||
import { DBDateFormat } from "../../../utils/dateFormats";
|
||||
|
||||
const SCHEDULED_POST_CHECK_INTERVAL = 5 * SECONDS;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import { postDueRemindersLoop } from "./utils/postDueRemindersLoop";
|
|||
import { RemindCmd } from "./commands/RemindCmd";
|
||||
import { RemindersCmd } from "./commands/RemindersCmd";
|
||||
import { RemindersDeleteCmd } from "./commands/RemindersDeleteCmd";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const defaultOptions: PluginOptions<RemindersPluginType> = {
|
||||
config: {
|
||||
|
@ -27,6 +28,7 @@ export const RemindersPlugin = zeppelinPlugin<RemindersPluginType>()("reminders"
|
|||
prettyName: "Reminders",
|
||||
},
|
||||
|
||||
dependencies: [TimeAndDatePlugin],
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@ import { convertDelayStringToMS, messageLink } from "src/utils";
|
|||
import humanizeDuration from "humanize-duration";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "src/pluginUtils";
|
||||
import { remindersCommand } from "../types";
|
||||
import { getGuildTz, inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export const RemindCmd = remindersCommand({
|
||||
trigger: ["remind", "remindme"],
|
||||
|
@ -18,8 +17,10 @@ export const RemindCmd = remindersCommand({
|
|||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
const now = moment.utc();
|
||||
const tz = getGuildTz(pluginData);
|
||||
const tz = await timeAndDate.getMemberTz(msg.author.id);
|
||||
|
||||
let reminderTime: moment.Moment;
|
||||
if (args.time.match(/^\d{4}-\d{1,2}-\d{1,2}$/)) {
|
||||
|
@ -62,7 +63,10 @@ export const RemindCmd = remindersCommand({
|
|||
|
||||
const msUntilReminder = reminderTime.diff(now);
|
||||
const timeUntilReminder = humanizeDuration(msUntilReminder, { largest: 2, round: true });
|
||||
const prettyReminderTime = inGuildTz(pluginData, reminderTime).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const prettyReminderTime = (await timeAndDate.inMemberTz(msg.author.id, reminderTime)).format(
|
||||
pluginData.getPlugin(TimeAndDatePlugin).getDateFormat("pretty_datetime"),
|
||||
);
|
||||
|
||||
sendSuccessMessage(
|
||||
pluginData,
|
||||
msg.channel,
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { remindersCommand } from "../types";
|
||||
import { sendErrorMessage } from "src/pluginUtils";
|
||||
import { createChunkedMessage, sorter } from "src/utils";
|
||||
import { createChunkedMessage, DBDateFormat, sorter } from "src/utils";
|
||||
import moment from "moment-timezone";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { DBDateFormat, getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export const RemindersCmd = remindersCommand({
|
||||
trigger: "reminders",
|
||||
|
@ -17,6 +16,8 @@ export const RemindersCmd = remindersCommand({
|
|||
return;
|
||||
}
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
reminders.sort(sorter("remind_at"));
|
||||
const longestNum = (reminders.length + 1).toString().length;
|
||||
const lines = Array.from(reminders.entries()).map(([i, reminder]) => {
|
||||
|
@ -25,9 +26,9 @@ export const RemindersCmd = remindersCommand({
|
|||
const target = moment.utc(reminder.remind_at, "YYYY-MM-DD HH:mm:ss");
|
||||
const diff = target.diff(moment.utc());
|
||||
const result = humanizeDuration(diff, { largest: 2, round: true });
|
||||
const prettyRemindAt = inGuildTz(pluginData, moment.utc(reminder.remind_at, DBDateFormat)).format(
|
||||
getDateFormat(pluginData, "pretty_datetime"),
|
||||
);
|
||||
const prettyRemindAt = timeAndDate
|
||||
.inGuildTz(moment.utc(reminder.remind_at, DBDateFormat))
|
||||
.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
return `\`${paddedNum}.\` \`${prettyRemindAt} (${result})\` ${reminder.body}`;
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { SavedMessage } from "src/data/entities/SavedMessage";
|
|||
import { RecentActionType, TBaseSingleSpamConfig, SpamPluginType } from "../types";
|
||||
import moment from "moment-timezone";
|
||||
import { MuteResult } from "src/plugins/Mutes/types";
|
||||
import { convertDelayStringToMS, trimLines, stripObjectToScalars, resolveMember, noop } from "src/utils";
|
||||
import { convertDelayStringToMS, trimLines, stripObjectToScalars, resolveMember, noop, DBDateFormat } from "src/utils";
|
||||
import { LogType } from "src/data/LogType";
|
||||
import { CaseTypes } from "src/data/CaseTypes";
|
||||
import { logger } from "src/logger";
|
||||
|
@ -14,7 +14,6 @@ import { getRecentActionCount } from "./getRecentActionCount";
|
|||
import { getRecentActions } from "./getRecentActions";
|
||||
import { clearRecentUserActions } from "./clearRecentUserActions";
|
||||
import { saveSpamArchives } from "./saveSpamArchives";
|
||||
import { DBDateFormat } from "../../../utils/dateFormats";
|
||||
|
||||
export async function logAndDetectMessageSpam(
|
||||
pluginData: PluginData<SpamPluginType>,
|
||||
|
|
|
@ -15,7 +15,7 @@ import { TagSourceCmd } from "./commands/TagSourceCmd";
|
|||
import moment from "moment-timezone";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { convertDelayStringToMS } from "../../utils";
|
||||
import { getGuildTz, inGuildTz } from "../../utils/timezones";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const defaultOptions: PluginOptions<TagsPluginType> = {
|
||||
config: {
|
||||
|
@ -77,7 +77,9 @@ export const TagsPlugin = zeppelinPlugin<TagsPluginType>()("tags", {
|
|||
state.onMessageDeleteFn = msg => onMessageDelete(pluginData, msg);
|
||||
state.savedMessages.events.on("delete", state.onMessageDeleteFn);
|
||||
|
||||
const tz = getGuildTz(pluginData);
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
const tz = timeAndDate.getGuildTz();
|
||||
state.tagFunctions = {
|
||||
parseDateTime(str) {
|
||||
if (typeof str === "number") {
|
||||
|
@ -154,13 +156,13 @@ export const TagsPlugin = zeppelinPlugin<TagsPluginType>()("tags", {
|
|||
|
||||
formatTime(time, format) {
|
||||
const parsed = this.parseDateTime(time);
|
||||
return inGuildTz(parsed).format(format);
|
||||
return timeAndDate.inGuildTz(parsed).format(format);
|
||||
},
|
||||
|
||||
discordDateFormat(time) {
|
||||
const parsed = time ? this.parseDateTime(time) : Date.now();
|
||||
|
||||
return inGuildTz(parsed).format("YYYY-MM-DD");
|
||||
return timeAndDate.inGuildTz(parsed).format("YYYY-MM-DD");
|
||||
},
|
||||
|
||||
mention: input => {
|
||||
|
|
50
backend/src/plugins/TimeAndDate/TimeAndDatePlugin.ts
Normal file
50
backend/src/plugins/TimeAndDate/TimeAndDatePlugin.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { zeppelinPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { ConfigSchema, TimeAndDatePluginType } from "./types";
|
||||
import { GuildMemberTimezones } from "../../data/GuildMemberTimezones";
|
||||
import { PluginOptions } from "knub";
|
||||
import { SetTimezoneCmd } from "./commands/SetTimezoneCmd";
|
||||
import { ViewTimezoneCmd } from "./commands/ViewTimezoneCmd";
|
||||
import { defaultDateFormats } from "./defaultDateFormats";
|
||||
import { Tail } from "../../utils/typeUtils";
|
||||
import { inGuildTz } from "./functions/inGuildTz";
|
||||
import { mapToPublicFn } from "../../pluginUtils";
|
||||
import { getGuildTz } from "./functions/getGuildTz";
|
||||
import { getMemberTz } from "./functions/getMemberTz";
|
||||
import { getDateFormat } from "./functions/getDateFormat";
|
||||
import { inMemberTz } from "./functions/inMemberTz";
|
||||
|
||||
const defaultOptions: PluginOptions<TimeAndDatePluginType> = {
|
||||
config: {
|
||||
timezone: "Etc/UTC",
|
||||
can_set_timezone: false,
|
||||
date_formats: defaultDateFormats,
|
||||
},
|
||||
|
||||
overrides: [
|
||||
{
|
||||
level: ">=50",
|
||||
config: {
|
||||
can_set_timezone: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const TimeAndDatePlugin = zeppelinPlugin<TimeAndDatePluginType>()("time_and_date", {
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
commands: [SetTimezoneCmd, ViewTimezoneCmd],
|
||||
|
||||
public: {
|
||||
getGuildTz: mapToPublicFn(getGuildTz),
|
||||
inGuildTz: mapToPublicFn(inGuildTz),
|
||||
getMemberTz: mapToPublicFn(getMemberTz),
|
||||
inMemberTz: mapToPublicFn(inMemberTz),
|
||||
getDateFormat: mapToPublicFn(getDateFormat),
|
||||
},
|
||||
|
||||
onLoad(pluginData) {
|
||||
pluginData.state.memberTimezones = GuildMemberTimezones.getGuildInstance(pluginData.guild.id);
|
||||
},
|
||||
});
|
17
backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts
Normal file
17
backend/src/plugins/TimeAndDate/commands/SetTimezoneCmd.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { timeAndDateCmd } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendSuccessMessage } from "../../../pluginUtils";
|
||||
|
||||
export const SetTimezoneCmd = timeAndDateCmd({
|
||||
trigger: "timezone",
|
||||
permission: "can_set_timezone",
|
||||
|
||||
signature: {
|
||||
timezone: ct.timezone(),
|
||||
},
|
||||
|
||||
async run({ pluginData, message, args }) {
|
||||
await pluginData.state.memberTimezones.set(message.author.id, args.timezone);
|
||||
sendSuccessMessage(pluginData, message.channel, `Your timezone is now set to **${args.timezone}**`);
|
||||
},
|
||||
});
|
23
backend/src/plugins/TimeAndDate/commands/ViewTimezoneCmd.ts
Normal file
23
backend/src/plugins/TimeAndDate/commands/ViewTimezoneCmd.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { timeAndDateCmd } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendSuccessMessage } from "../../../pluginUtils";
|
||||
import { getMemberTz } from "../functions/getMemberTz";
|
||||
import { getGuildTz } from "../functions/getGuildTz";
|
||||
|
||||
export const ViewTimezoneCmd = timeAndDateCmd({
|
||||
trigger: "timezone",
|
||||
permission: "can_set_timezone",
|
||||
|
||||
signature: {},
|
||||
|
||||
async run({ pluginData, message, args }) {
|
||||
const memberTimezone = await pluginData.state.memberTimezones.get(message.author.id);
|
||||
if (memberTimezone) {
|
||||
message.channel.createMessage(`Your timezone is currently set to **${memberTimezone.timezone}**`);
|
||||
return;
|
||||
}
|
||||
|
||||
const serverTimezone = getGuildTz(pluginData);
|
||||
message.channel.createMessage(`Your timezone is currently set to **${serverTimezone}** (server default)`);
|
||||
},
|
||||
});
|
5
backend/src/plugins/TimeAndDate/defaultDateFormats.ts
Normal file
5
backend/src/plugins/TimeAndDate/defaultDateFormats.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export const defaultDateFormats = {
|
||||
date: "MMM D, YYYY",
|
||||
time: "H:mm",
|
||||
pretty_datetime: "MMM D, YYYY [at] H:mm z",
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
import { PluginData } from "knub";
|
||||
import { defaultDateFormats } from "../defaultDateFormats";
|
||||
|
||||
export function getDateFormat(pluginData: PluginData<any>, formatName: keyof typeof defaultDateFormats) {
|
||||
return pluginData.config.get().date_formats?.[formatName] || defaultDateFormats[formatName];
|
||||
}
|
7
backend/src/plugins/TimeAndDate/functions/getGuildTz.ts
Normal file
7
backend/src/plugins/TimeAndDate/functions/getGuildTz.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { PluginData } from "knub";
|
||||
import { ZeppelinGuildConfig } from "../../../types";
|
||||
import { TimeAndDatePluginType } from "../types";
|
||||
|
||||
export function getGuildTz(pluginData: PluginData<TimeAndDatePluginType>) {
|
||||
return pluginData.config.get().timezone;
|
||||
}
|
8
backend/src/plugins/TimeAndDate/functions/getMemberTz.ts
Normal file
8
backend/src/plugins/TimeAndDate/functions/getMemberTz.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { PluginData } from "knub";
|
||||
import { TimeAndDatePluginType } from "../types";
|
||||
import { getGuildTz } from "./getGuildTz";
|
||||
|
||||
export async function getMemberTz(pluginData: PluginData<TimeAndDatePluginType>, memberId: string) {
|
||||
const memberTz = await pluginData.state.memberTimezones.get(memberId);
|
||||
return memberTz?.timezone || getGuildTz(pluginData);
|
||||
}
|
|
@ -1,13 +1,9 @@
|
|||
import moment from "moment-timezone";
|
||||
import { PluginData } from "knub";
|
||||
import { ZeppelinGuildConfig } from "../types";
|
||||
import { TimeAndDatePluginType } from "../types";
|
||||
import moment from "moment-timezone";
|
||||
import { getGuildTz } from "./getGuildTz";
|
||||
|
||||
export function getGuildTz(pluginData: PluginData<any>) {
|
||||
const guildConfig = pluginData.guildConfig as ZeppelinGuildConfig;
|
||||
return guildConfig.timezone || "Etc/UTC";
|
||||
}
|
||||
|
||||
export function inGuildTz(pluginData: PluginData<any>, input?: moment.Moment | number) {
|
||||
export function inGuildTz(pluginData: PluginData<TimeAndDatePluginType>, input?: moment.Moment | number) {
|
||||
let momentObj: moment.Moment;
|
||||
if (typeof input === "number") {
|
||||
momentObj = moment.utc(input, "x");
|
22
backend/src/plugins/TimeAndDate/functions/inMemberTz.ts
Normal file
22
backend/src/plugins/TimeAndDate/functions/inMemberTz.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { PluginData } from "knub";
|
||||
import { TimeAndDatePluginType } from "../types";
|
||||
import moment from "moment-timezone";
|
||||
import { getGuildTz } from "./getGuildTz";
|
||||
import { getMemberTz } from "./getMemberTz";
|
||||
|
||||
export async function inMemberTz(
|
||||
pluginData: PluginData<TimeAndDatePluginType>,
|
||||
memberId: string,
|
||||
input?: moment.Moment | number,
|
||||
) {
|
||||
let momentObj: moment.Moment;
|
||||
if (typeof input === "number") {
|
||||
momentObj = moment.utc(input, "x");
|
||||
} else if (moment.isMoment(input)) {
|
||||
momentObj = input.clone();
|
||||
} else {
|
||||
momentObj = moment.utc();
|
||||
}
|
||||
|
||||
return momentObj.tz(await getMemberTz(pluginData, memberId));
|
||||
}
|
22
backend/src/plugins/TimeAndDate/types.ts
Normal file
22
backend/src/plugins/TimeAndDate/types.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import * as t from "io-ts";
|
||||
import { tNullable, tPartialDictionary } from "../../utils";
|
||||
import { BasePluginType, command } from "knub";
|
||||
import { GuildMemberTimezones } from "../../data/GuildMemberTimezones";
|
||||
import { tValidTimezone } from "../../utils/tValidTimezone";
|
||||
import { defaultDateFormats } from "./defaultDateFormats";
|
||||
|
||||
export const ConfigSchema = t.type({
|
||||
timezone: tValidTimezone,
|
||||
date_formats: tNullable(tPartialDictionary(t.keyof(defaultDateFormats), t.string)),
|
||||
can_set_timezone: t.boolean,
|
||||
});
|
||||
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||
|
||||
export interface TimeAndDatePluginType extends BasePluginType {
|
||||
config: TConfigSchema;
|
||||
state: {
|
||||
memberTimezones: GuildMemberTimezones;
|
||||
};
|
||||
}
|
||||
|
||||
export const timeAndDateCmd = command<TimeAndDatePluginType>();
|
|
@ -33,6 +33,7 @@ import { MessageInfoCmd } from "./commands/MessageInfoCmd";
|
|||
import { InfoCmd } from "./commands/InfoCmd";
|
||||
import { SnowflakeInfoCmd } from "./commands/SnowflakeInfoCmd";
|
||||
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
|
||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const defaultOptions: PluginOptions<UtilityPluginType> = {
|
||||
config: {
|
||||
|
@ -101,6 +102,7 @@ export const UtilityPlugin = zeppelinPlugin<UtilityPluginType>()("utility", {
|
|||
prettyName: "Utility",
|
||||
},
|
||||
|
||||
dependencies: [TimeAndDatePlugin],
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
|
|
|
@ -6,9 +6,8 @@ import humanizeDuration from "humanize-duration";
|
|||
import LCL from "last-commit-log";
|
||||
import path from "path";
|
||||
import moment from "moment-timezone";
|
||||
import { getGuildTz, inGuildTz } from "../../../utils/timezones";
|
||||
import { rootDir } from "../../../paths";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export const AboutCmd = utilityCmd({
|
||||
trigger: "about",
|
||||
|
@ -16,6 +15,8 @@ export const AboutCmd = utilityCmd({
|
|||
permission: "can_about",
|
||||
|
||||
async run({ message: msg, pluginData }) {
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
const uptime = getCurrentUptime();
|
||||
const prettyUptime = humanizeDuration(uptime, { largest: 2, round: true });
|
||||
|
||||
|
@ -30,9 +31,9 @@ export const AboutCmd = utilityCmd({
|
|||
let version;
|
||||
|
||||
if (lastCommit) {
|
||||
lastUpdate = inGuildTz(pluginData, moment.utc(lastCommit.committer.date, "X")).format(
|
||||
getDateFormat(pluginData, "pretty_datetime"),
|
||||
);
|
||||
lastUpdate = timeAndDate
|
||||
.inGuildTz(moment.utc(lastCommit.committer.date, "X"))
|
||||
.format(pluginData.getPlugin(TimeAndDatePlugin).getDateFormat("pretty_datetime"));
|
||||
version = lastCommit.shortHash;
|
||||
} else {
|
||||
lastUpdate = "?";
|
||||
|
@ -52,7 +53,7 @@ export const AboutCmd = utilityCmd({
|
|||
["Last update", lastUpdate],
|
||||
["Version", version],
|
||||
["API latency", `${shard.latency}ms`],
|
||||
["Server timezone", getGuildTz(pluginData)],
|
||||
["Server timezone", timeAndDate.getGuildTz()],
|
||||
];
|
||||
|
||||
const loadedPlugins = Array.from(
|
||||
|
|
|
@ -32,7 +32,7 @@ export const InfoCmd = utilityCmd({
|
|||
const channelId = getChannelId(value);
|
||||
const channel = channelId && pluginData.guild.channels.get(channelId);
|
||||
if (channel) {
|
||||
const embed = await getChannelInfoEmbed(pluginData, channelId);
|
||||
const embed = await getChannelInfoEmbed(pluginData, channelId, message.author.id);
|
||||
if (embed) {
|
||||
message.channel.createMessage({ embed });
|
||||
return;
|
||||
|
@ -42,7 +42,7 @@ export const InfoCmd = utilityCmd({
|
|||
// 2. Server
|
||||
const guild = pluginData.client.guilds.get(value);
|
||||
if (guild) {
|
||||
const embed = await getServerInfoEmbed(pluginData, value);
|
||||
const embed = await getServerInfoEmbed(pluginData, value, message.author.id);
|
||||
if (embed) {
|
||||
message.channel.createMessage({ embed });
|
||||
return;
|
||||
|
@ -52,7 +52,7 @@ export const InfoCmd = utilityCmd({
|
|||
// 3. User
|
||||
const user = await resolveUser(pluginData.client, value);
|
||||
if (user) {
|
||||
const embed = await getUserInfoEmbed(pluginData, user.id, Boolean(args.compact));
|
||||
const embed = await getUserInfoEmbed(pluginData, user.id, Boolean(args.compact), message.author.id);
|
||||
if (embed) {
|
||||
message.channel.createMessage({ embed });
|
||||
return;
|
||||
|
@ -63,7 +63,12 @@ export const InfoCmd = utilityCmd({
|
|||
const messageTarget = await resolveMessageTarget(pluginData, value);
|
||||
if (messageTarget) {
|
||||
if (canReadChannel(messageTarget.channel, message.member)) {
|
||||
const embed = await getMessageInfoEmbed(pluginData, messageTarget.channel.id, messageTarget.messageId);
|
||||
const embed = await getMessageInfoEmbed(
|
||||
pluginData,
|
||||
messageTarget.channel.id,
|
||||
messageTarget.messageId,
|
||||
message.author.id,
|
||||
);
|
||||
if (embed) {
|
||||
message.channel.createMessage({ embed });
|
||||
return;
|
||||
|
@ -87,7 +92,7 @@ export const InfoCmd = utilityCmd({
|
|||
// 6. Server again (fallback for discovery servers)
|
||||
const serverPreview = getGuildPreview(pluginData.client, value).catch(() => null);
|
||||
if (serverPreview) {
|
||||
const embed = await getServerInfoEmbed(pluginData, value);
|
||||
const embed = await getServerInfoEmbed(pluginData, value, message.author.id);
|
||||
if (embed) {
|
||||
message.channel.createMessage({ embed });
|
||||
return;
|
||||
|
@ -96,7 +101,7 @@ export const InfoCmd = utilityCmd({
|
|||
|
||||
// 7. Arbitrary ID
|
||||
if (isValidSnowflake(value)) {
|
||||
const embed = getSnowflakeInfoEmbed(pluginData, value, true);
|
||||
const embed = await getSnowflakeInfoEmbed(pluginData, value, true, message.author.id);
|
||||
message.channel.createMessage({ embed });
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,12 @@ export const MessageInfoCmd = utilityCmd({
|
|||
return;
|
||||
}
|
||||
|
||||
const embed = await getMessageInfoEmbed(pluginData, args.message.channel.id, args.message.messageId);
|
||||
const embed = await getMessageInfoEmbed(
|
||||
pluginData,
|
||||
args.message.channel.id,
|
||||
args.message.messageId,
|
||||
message.author.id,
|
||||
);
|
||||
if (!embed) {
|
||||
sendErrorMessage(pluginData, message.channel, "Unknown message");
|
||||
return;
|
||||
|
|
|
@ -15,7 +15,7 @@ export const ServerCmd = utilityCmd({
|
|||
|
||||
async run({ message, pluginData, args }) {
|
||||
const serverId = args.serverId || pluginData.guild.id;
|
||||
const serverInfoEmbed = await getServerInfoEmbed(pluginData, serverId);
|
||||
const serverInfoEmbed = await getServerInfoEmbed(pluginData, serverId, message.author.id);
|
||||
if (!serverInfoEmbed) {
|
||||
sendErrorMessage(pluginData, message.channel, "Could not find information for that server");
|
||||
return;
|
||||
|
|
|
@ -14,8 +14,8 @@ export const SnowflakeInfoCmd = utilityCmd({
|
|||
id: ct.anyId(),
|
||||
},
|
||||
|
||||
run({ message, args, pluginData }) {
|
||||
const embed = getSnowflakeInfoEmbed(pluginData, args.id);
|
||||
async run({ message, args, pluginData }) {
|
||||
const embed = await getSnowflakeInfoEmbed(pluginData, args.id, false, message.author.id);
|
||||
message.channel.createMessage({ embed });
|
||||
},
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ export const UserInfoCmd = utilityCmd({
|
|||
|
||||
async run({ message, args, pluginData }) {
|
||||
const userId = args.user?.id || message.author.id;
|
||||
const embed = await getUserInfoEmbed(pluginData, userId, args.compact);
|
||||
const embed = await getUserInfoEmbed(pluginData, userId, args.compact, message.author.id);
|
||||
if (!embed) {
|
||||
sendErrorMessage(pluginData, message.channel, "User not found");
|
||||
return;
|
||||
|
|
|
@ -4,8 +4,7 @@ import { Constants, EmbedOptions } from "eris";
|
|||
import moment from "moment-timezone";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { formatNumber, preEmbedPadding, trimLines } from "../../../utils";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const TEXT_CHANNEL_ICON =
|
||||
"https://cdn.discordapp.com/attachments/740650744830623756/740656843545772062/text-channel.png";
|
||||
|
@ -17,6 +16,7 @@ const ANNOUNCEMENT_CHANNEL_ICON =
|
|||
export async function getChannelInfoEmbed(
|
||||
pluginData: PluginData<UtilityPluginType>,
|
||||
channelId: string,
|
||||
requestMemberId?: string,
|
||||
): Promise<EmbedOptions | null> {
|
||||
const channel = pluginData.guild.channels.get(channelId);
|
||||
if (!channel) {
|
||||
|
@ -58,7 +58,11 @@ export async function getChannelInfoEmbed(
|
|||
}
|
||||
|
||||
const createdAt = moment.utc(channel.createdAt, "x");
|
||||
const prettyCreatedAt = inGuildTz(pluginData, createdAt).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
const tzCreatedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, createdAt)
|
||||
: timeAndDate.inGuildTz(createdAt);
|
||||
const prettyCreatedAt = tzCreatedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const channelAge = humanizeDuration(Date.now() - channel.createdAt, {
|
||||
largest: 2,
|
||||
round: true,
|
||||
|
|
|
@ -5,8 +5,7 @@ import moment from "moment-timezone";
|
|||
import humanizeDuration from "humanize-duration";
|
||||
import { chunkMessageLines, messageLink, preEmbedPadding, trimEmptyLines, trimLines } from "../../../utils";
|
||||
import { getDefaultPrefix } from "knub/dist/commands/commandUtils";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const MESSAGE_ICON = "https://cdn.discordapp.com/attachments/740650744830623756/740685652152025088/message.png";
|
||||
|
||||
|
@ -14,12 +13,15 @@ export async function getMessageInfoEmbed(
|
|||
pluginData: PluginData<UtilityPluginType>,
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
requestMemberId?: string,
|
||||
): Promise<EmbedOptions | null> {
|
||||
const message = await pluginData.client.getMessage(channelId, messageId).catch(() => null);
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
const embed: EmbedOptions = {
|
||||
fields: [],
|
||||
};
|
||||
|
@ -30,14 +32,20 @@ export async function getMessageInfoEmbed(
|
|||
};
|
||||
|
||||
const createdAt = moment.utc(message.createdAt, "x");
|
||||
const prettyCreatedAt = inGuildTz(pluginData, createdAt).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const tzCreatedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, createdAt)
|
||||
: timeAndDate.inGuildTz(createdAt);
|
||||
const prettyCreatedAt = tzCreatedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const messageAge = humanizeDuration(Date.now() - message.createdAt, {
|
||||
largest: 2,
|
||||
round: true,
|
||||
});
|
||||
|
||||
const editedAt = message.editedTimestamp && moment.utc(message.editedTimestamp, "x");
|
||||
const prettyEditedAt = inGuildTz(pluginData, editedAt).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const tzEditedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, editedAt)
|
||||
: timeAndDate.inGuildTz(editedAt);
|
||||
const prettyEditedAt = tzEditedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const editAge =
|
||||
message.editedTimestamp &&
|
||||
humanizeDuration(Date.now() - message.editedTimestamp, {
|
||||
|
@ -75,18 +83,20 @@ export async function getMessageInfoEmbed(
|
|||
});
|
||||
|
||||
const authorCreatedAt = moment.utc(message.author.createdAt, "x");
|
||||
const prettyAuthorCreatedAt = inGuildTz(pluginData, authorCreatedAt).format(
|
||||
getDateFormat(pluginData, "pretty_datetime"),
|
||||
);
|
||||
const tzAuthorCreatedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, authorCreatedAt)
|
||||
: timeAndDate.inGuildTz(authorCreatedAt);
|
||||
const prettyAuthorCreatedAt = tzAuthorCreatedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const authorAccountAge = humanizeDuration(Date.now() - message.author.createdAt, {
|
||||
largest: 2,
|
||||
round: true,
|
||||
});
|
||||
|
||||
const authorJoinedAt = message.member && moment.utc(message.member.joinedAt, "x");
|
||||
const prettyAuthorJoinedAt = inGuildTz(pluginData, authorJoinedAt).format(
|
||||
getDateFormat(pluginData, "pretty_datetime"),
|
||||
);
|
||||
const tzAuthorJoinedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, authorJoinedAt)
|
||||
: timeAndDate.inGuildTz(authorJoinedAt);
|
||||
const prettyAuthorJoinedAt = tzAuthorJoinedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const authorServerAge =
|
||||
message.member &&
|
||||
humanizeDuration(Date.now() - message.member.joinedAt, {
|
||||
|
|
|
@ -5,12 +5,12 @@ import { CategoryChannel, EmbedOptions, Guild, RESTChannelInvite, TextChannel, V
|
|||
import moment from "moment-timezone";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { getGuildPreview } from "./getGuildPreview";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export async function getServerInfoEmbed(
|
||||
pluginData: PluginData<UtilityPluginType>,
|
||||
serverId: string,
|
||||
requestMemberId?: string,
|
||||
): Promise<EmbedOptions> {
|
||||
const thisServer = serverId === pluginData.guild.id ? pluginData.guild : null;
|
||||
const [restGuild, guildPreview] = await Promise.all([
|
||||
|
@ -39,8 +39,12 @@ export async function getServerInfoEmbed(
|
|||
};
|
||||
|
||||
// BASIC INFORMATION
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
const createdAt = moment.utc((guildPreview || restGuild).createdAt, "x");
|
||||
const prettyCreatedAt = inGuildTz(pluginData, createdAt).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const tzCreatedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, createdAt)
|
||||
: timeAndDate.inGuildTz(createdAt);
|
||||
const prettyCreatedAt = tzCreatedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const serverAge = humanizeDuration(moment.utc().valueOf() - createdAt.valueOf(), {
|
||||
largest: 2,
|
||||
round: true,
|
||||
|
|
|
@ -6,16 +6,16 @@ import moment from "moment-timezone";
|
|||
import { CaseTypes } from "src/data/CaseTypes";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { snowflakeToTimestamp } from "../../../utils/snowflakeToTimestamp";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
const SNOWFLAKE_ICON = "https://cdn.discordapp.com/attachments/740650744830623756/742020790471491668/snowflake.png";
|
||||
|
||||
export function getSnowflakeInfoEmbed(
|
||||
export async function getSnowflakeInfoEmbed(
|
||||
pluginData: PluginData<UtilityPluginType>,
|
||||
snowflake: string,
|
||||
showUnknownWarning = false,
|
||||
): EmbedOptions {
|
||||
requestMemberId?: string,
|
||||
): Promise<EmbedOptions> {
|
||||
const embed: EmbedOptions = {
|
||||
fields: [],
|
||||
};
|
||||
|
@ -30,9 +30,13 @@ export function getSnowflakeInfoEmbed(
|
|||
"This is a valid [snowflake ID](https://discord.com/developers/docs/reference#snowflakes), but I don't know what it's for.";
|
||||
}
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
const createdAtMS = snowflakeToTimestamp(snowflake);
|
||||
const createdAt = moment.utc(createdAtMS, "x");
|
||||
const prettyCreatedAt = inGuildTz(pluginData, createdAt).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const tzCreatedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, createdAt)
|
||||
: timeAndDate.inGuildTz(createdAt);
|
||||
const prettyCreatedAt = tzCreatedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const snowflakeAge = humanizeDuration(Date.now() - createdAtMS, {
|
||||
largest: 2,
|
||||
round: true,
|
||||
|
|
|
@ -14,13 +14,13 @@ import {
|
|||
import moment from "moment-timezone";
|
||||
import { CaseTypes } from "src/data/CaseTypes";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import { inGuildTz } from "../../../utils/timezones";
|
||||
import { getDateFormat } from "../../../utils/dateFormats";
|
||||
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
export async function getUserInfoEmbed(
|
||||
pluginData: PluginData<UtilityPluginType>,
|
||||
userId: string,
|
||||
compact = false,
|
||||
requestMemberId?: string,
|
||||
): Promise<EmbedOptions | null> {
|
||||
const user = await resolveUser(pluginData.client, userId);
|
||||
if (!user || user instanceof UnknownUser) {
|
||||
|
@ -33,6 +33,8 @@ export async function getUserInfoEmbed(
|
|||
fields: [],
|
||||
};
|
||||
|
||||
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
|
||||
|
||||
embed.author = {
|
||||
name: `User: ${user.username}#${user.discriminator}`,
|
||||
};
|
||||
|
@ -41,7 +43,10 @@ export async function getUserInfoEmbed(
|
|||
embed.author.icon_url = avatarURL;
|
||||
|
||||
const createdAt = moment.utc(user.createdAt, "x");
|
||||
const prettyCreatedAt = inGuildTz(pluginData, createdAt).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const tzCreatedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, createdAt)
|
||||
: timeAndDate.inGuildTz(createdAt);
|
||||
const prettyCreatedAt = tzCreatedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const accountAge = humanizeDuration(moment.utc().valueOf() - user.createdAt, {
|
||||
largest: 2,
|
||||
round: true,
|
||||
|
@ -57,7 +62,10 @@ export async function getUserInfoEmbed(
|
|||
});
|
||||
if (member) {
|
||||
const joinedAt = moment.utc(member.joinedAt, "x");
|
||||
const prettyJoinedAt = inGuildTz(pluginData, joinedAt).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const tzJoinedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, joinedAt)
|
||||
: timeAndDate.inGuildTz(joinedAt);
|
||||
const prettyJoinedAt = tzJoinedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const joinAge = humanizeDuration(moment.utc().valueOf() - member.joinedAt, {
|
||||
largest: 2,
|
||||
round: true,
|
||||
|
@ -85,7 +93,10 @@ export async function getUserInfoEmbed(
|
|||
|
||||
if (member) {
|
||||
const joinedAt = moment.utc(member.joinedAt, "x");
|
||||
const prettyJoinedAt = inGuildTz(pluginData, joinedAt).format(getDateFormat(pluginData, "pretty_datetime"));
|
||||
const tzJoinedAt = requestMemberId
|
||||
? await timeAndDate.inMemberTz(requestMemberId, joinedAt)
|
||||
: timeAndDate.inGuildTz(joinedAt);
|
||||
const prettyJoinedAt = tzJoinedAt.format(timeAndDate.getDateFormat("pretty_datetime"));
|
||||
const joinAge = humanizeDuration(moment.utc().valueOf() - member.joinedAt, {
|
||||
largest: 2,
|
||||
round: true,
|
||||
|
|
|
@ -31,6 +31,7 @@ import { CompanionChannelsPlugin } from "./CompanionChannels/CompanionChannelsPl
|
|||
import { CustomEventsPlugin } from "./CustomEvents/CustomEventsPlugin";
|
||||
import { BotControlPlugin } from "./BotControl/BotControlPlugin";
|
||||
import { GuildAccessMonitorPlugin } from "./GuildAccessMonitor/GuildAccessMonitorPlugin";
|
||||
import { TimeAndDatePlugin } from "./TimeAndDate/TimeAndDatePlugin";
|
||||
|
||||
// prettier-ignore
|
||||
export const guildPlugins: Array<ZeppelinPluginBlueprint<any>> = [
|
||||
|
@ -63,6 +64,7 @@ export const guildPlugins: Array<ZeppelinPluginBlueprint<any>> = [
|
|||
AutomodPlugin,
|
||||
CompanionChannelsPlugin,
|
||||
CustomEventsPlugin,
|
||||
TimeAndDatePlugin,
|
||||
];
|
||||
|
||||
// prettier-ignore
|
||||
|
@ -79,5 +81,6 @@ export const baseGuildPlugins: Array<ZeppelinPluginBlueprint<any>> = [
|
|||
NameHistoryPlugin,
|
||||
CasesPlugin,
|
||||
MutesPlugin,
|
||||
TimeAndDatePlugin,
|
||||
// TODO: Replace these with proper dependencies
|
||||
];
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
import { BaseConfig, Knub } from "knub";
|
||||
import * as t from "io-ts";
|
||||
|
||||
export const DateFormatsSchema = t.type({
|
||||
date: t.string,
|
||||
time: t.string,
|
||||
pretty_datetime: t.string,
|
||||
});
|
||||
|
||||
export type DateFormats = t.TypeOf<typeof DateFormatsSchema>;
|
||||
|
||||
export interface ZeppelinGuildConfig extends BaseConfig<any> {
|
||||
success_emoji?: string;
|
||||
error_emoji?: string;
|
||||
|
||||
// Deprecated
|
||||
timezone?: string;
|
||||
date_formats?: Partial<DateFormats>;
|
||||
date_formats?: any;
|
||||
}
|
||||
|
||||
export const ZeppelinGuildConfigSchema = t.type({
|
||||
|
@ -25,8 +19,10 @@ export const ZeppelinGuildConfigSchema = t.type({
|
|||
// From ZeppelinGuildConfig
|
||||
success_emoji: t.string,
|
||||
error_emoji: t.string,
|
||||
|
||||
// Deprecated
|
||||
timezone: t.string,
|
||||
date_formats: t.partial(DateFormatsSchema.props),
|
||||
date_formats: t.unknown,
|
||||
});
|
||||
export const PartialZeppelinGuildConfigSchema = t.partial(ZeppelinGuildConfigSchema.props);
|
||||
|
||||
|
|
|
@ -1298,3 +1298,5 @@ export function asyncMap<T, R>(arr: T[], fn: (item: T) => Promise<R>): Promise<R
|
|||
export function unique<T>(arr: T[]): T[] {
|
||||
return Array.from(new Set(arr));
|
||||
}
|
||||
|
||||
export const DBDateFormat = "YYYY-MM-DD HH:mm:ss";
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import { PluginData } from "knub";
|
||||
import { DateFormats } from "../types";
|
||||
|
||||
const defaultDateFormats: DateFormats = {
|
||||
date: "MMM D, YYYY",
|
||||
time: "H:mm",
|
||||
pretty_datetime: "MMM D, YYYY [at] H:mm z",
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the guild-specific date format, falling back to the defaults if one has not been specified
|
||||
*/
|
||||
export function getDateFormat(pluginData: PluginData<any>, formatName: keyof DateFormats) {
|
||||
return pluginData.guildConfig.date_formats?.[formatName] || defaultDateFormats[formatName];
|
||||
}
|
||||
|
||||
export const DBDateFormat = "YYYY-MM-DD HH:mm:ss";
|
7
backend/src/utils/isValidTimezone.ts
Normal file
7
backend/src/utils/isValidTimezone.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import moment from "moment-timezone";
|
||||
|
||||
const validTimezones = moment.tz.names();
|
||||
|
||||
export function isValidTimezone(input: string) {
|
||||
return validTimezones.includes(input);
|
||||
}
|
13
backend/src/utils/tValidTimezone.ts
Normal file
13
backend/src/utils/tValidTimezone.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import * as t from "io-ts";
|
||||
import { either } from "fp-ts/lib/Either";
|
||||
import { isValidTimezone } from "./isValidTimezone";
|
||||
|
||||
export const tValidTimezone = new t.Type<string, string>(
|
||||
"tValidTimezone",
|
||||
(s): s is string => typeof s === "string",
|
||||
(from, to) =>
|
||||
either.chain(t.string.validate(from, to), input => {
|
||||
return isValidTimezone(input) ? t.success(input) : t.failure(from, to, `Invalid timezone: ${input}`);
|
||||
}),
|
||||
s => s,
|
||||
);
|
2
backend/src/utils/typeUtils.ts
Normal file
2
backend/src/utils/typeUtils.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
// From https://stackoverflow.com/a/56370310/316944
|
||||
export type Tail<T extends any[]> = ((...t: T) => void) extends (h: any, ...r: infer R) => void ? R : never;
|
Loading…
Add table
Reference in a new issue