Skip to content

Commit

Permalink
fix(discord): now discord notifier shows user list with correct link url
Browse files Browse the repository at this point in the history
  • Loading branch information
async3619 committed Dec 8, 2022
1 parent eab3be9 commit fbc278f
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 90 deletions.
7 changes: 6 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,13 @@ export class App extends Loggable {
return;
}

const watcherMap = this.config.watcherMap;
for (const notifier of notifiers) {
await notifier.notify(newLogs);
await notifier.notify(
newLogs.map(log => {
return [watcherMap[log.user.from], log];
}),
);
}
};
}
7 changes: 6 additions & 1 deletion src/notifiers/base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { UserLog } from "@repositories/models/user-log";

import { BaseWatcher } from "@watchers/base";

import { Loggable } from "@utils/types";

export type NotifyPair = [BaseWatcher<string>, UserLog];

export abstract class BaseNotifier extends Loggable {
protected constructor(name: string) {
super(name);
Expand All @@ -12,5 +17,5 @@ export abstract class BaseNotifier extends Loggable {

public abstract initialize(options: Record<string, any>): Promise<void>;

public abstract notify(logs: UserLog[]): Promise<void>;
public abstract notify(logs: NotifyPair[]): Promise<void>;
}
87 changes: 49 additions & 38 deletions src/notifiers/discord.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import pluralize from "pluralize";
import dayjs from "dayjs";

import { User } from "@repositories/models/user";
import { UserLog, UserLogType } from "@repositories/models/user-log";

import { BaseNotifier } from "@notifiers/base";
import { BaseNotifier, NotifyPair } from "@notifiers/base";
import { BaseWatcher } from "@watchers/base";

import { Fetcher } from "@utils/fetcher";
import { Logger } from "@utils/logger";
import dayjs from "dayjs";

export interface DiscordNotifierOptions {
type: "discord";
Expand Down Expand Up @@ -43,12 +44,12 @@ export class DiscordNotifier extends BaseNotifier {
this.webhookUrl = options.webhookUrl;
}

public async notify(logs: UserLog[]) {
public async notify(pairs: NotifyPair[]) {
if (!this.webhookUrl) {
throw new Error("DiscordNotifier is not initialized");
}

if (logs.length <= 0) {
if (pairs.length <= 0) {
return;
}

Expand All @@ -58,9 +59,9 @@ export class DiscordNotifier extends BaseNotifier {
{
title: Logger.format(
"Total {} {} {} found",
logs.length,
pluralize("change", logs.length),
pluralize("was", logs.length),
pairs.length,
pluralize("change", pairs.length),
pluralize("was", pairs.length),
),
color: 5814783,
fields: [],
Expand All @@ -74,40 +75,26 @@ export class DiscordNotifier extends BaseNotifier {
attachments: [],
};

const newFields: DiscordWebhookData["embeds"][0]["fields"] = [];
const followerLogs = logs.filter(l => l.type === UserLogType.Follow);
if (followerLogs.length > 0) {
const field = this.generateEmbedField(
followerLogs,
Logger.format("🎉 {} new {}", followerLogs.length, pluralize("follower", logs.length)),
);
const fields: DiscordWebhookData["embeds"][0]["fields"] = [];
const followerLogs = pairs.filter(([, l]) => l.type === UserLogType.Follow);
const unfollowerLogs = pairs.filter(([, l]) => l.type === UserLogType.Unfollow);
const renameLogs = pairs.filter(
([, l]) => l.type === UserLogType.RenameUserId || l.type === UserLogType.RenameDisplayName,
);

newFields.push(field);
if (followerLogs.length > 0) {
fields.push(this.composeLogs(followerLogs, "🎉 {} new {}", "follower"));
}

const unfollowerLogs = logs.filter(l => l.type === UserLogType.Unfollow);
if (unfollowerLogs.length > 0) {
const field = this.generateEmbedField(
unfollowerLogs,
Logger.format("❌ {} {}", unfollowerLogs.length, pluralize("unfollower", logs.length)),
);

newFields.push(field);
fields.push(this.composeLogs(unfollowerLogs, "❌ {} {}", "unfollower"));
}

const renameLogs = logs.filter(
l => l.type === UserLogType.RenameUserId || l.type === UserLogType.RenameDisplayName,
);
if (renameLogs.length > 0) {
const field = this.generateEmbedField(
renameLogs,
Logger.format("✏️ {} {}", renameLogs.length, pluralize("rename", logs.length)),
);

newFields.push(field);
fields.push(this.composeLogs(renameLogs, "✏️ {} {}", "rename"));
}

data.embeds[0].fields.push(...newFields);
data.embeds[0].fields.push(...fields);

await this.fetcher.fetch({
url: this.webhookUrl,
Expand All @@ -116,28 +103,52 @@ export class DiscordNotifier extends BaseNotifier {
});
}

private generateEmbedField(logs: UserLog[], title: string) {
private composeLogs(
logs: NotifyPair[],
messageFormat: string,
word: string,
): DiscordWebhookData["embeds"][0]["fields"][0] {
const { name, value } = this.generateEmbedField(
logs,
Logger.format(messageFormat, logs.length, pluralize(word, logs.length)),
);

const valueLines = [value];
if (logs.length > 10) {
valueLines.push(`_... and ${logs.length - 10} more_`);
}

return {
name,
value: valueLines.join("\n"),
};
}

private generateEmbedField(logs: NotifyPair[], title: string) {
return {
name: title,
value: logs
.map<[User, UserLog]>(l => [l.user, l])
.map<[BaseWatcher<string>, User, UserLog]>(([w, l]) => [w, l.user, l])
.slice(0, 10)
.map(([user, log]) => {
.map(([watcher, user, log]) => {
if (log.type === UserLogType.RenameUserId || log.type === UserLogType.RenameDisplayName) {
return Logger.format(
"{} (@{}) → {}{}",
"[{}] [{} (@{})]({}) → {}{}",
watcher.getName(),
log.oldDisplayName,
log.oldUserId,
watcher.getProfileUrl(log.user),
log.type === UserLogType.RenameDisplayName ? "" : "@",
log.type === UserLogType.RenameUserId ? user.userId : user.displayName,
);
}

return Logger.format(
"[{} (@{})](https://twitter.com/{})",
"[{}] [{} (@{})]({})",
watcher.getName(),
user.displayName,
user.userId,
user.userId,
watcher.getProfileUrl(user),
);
})
.join("\n"),
Expand Down
4 changes: 4 additions & 0 deletions src/watchers/base.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pluralize from "pluralize";
import { UserData } from "@repositories/models/user";

import { WatcherTypes } from "@watchers";
Expand All @@ -20,11 +21,14 @@ export abstract class BaseWatcher<TType extends string> extends Loggable<TType>
public async doWatch(): Promise<UserData[]> {
const followers = await this.getFollowers();

this.logger.info("Successfully crawled {} {}", [followers.length, pluralize("follower", followers.length)]);

return followers.map(user => ({
...user,
from: this.getName().toLowerCase(),
}));
}

protected abstract getFollowers(): Promise<PartialUserData[]>;
public abstract getProfileUrl(user: UserData): string;
}
8 changes: 6 additions & 2 deletions src/watchers/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ export class GitHubWatcher extends BaseWatcher<"GitHub"> {
public async initialize() {
return;
}
public async getFollowers() {
public getProfileUrl(user: PartialUserData) {
return `https://github.com/${user.userId}`;
}

protected async getFollowers() {
try {
const result: PartialUserData[] = [];
const currentUserId = await this.getCurrentUserId();
Expand Down Expand Up @@ -105,7 +109,7 @@ export class GitHubWatcher extends BaseWatcher<"GitHub"> {

return [
data.user.followers.edges
.map(edge => {
.map<PartialUserData | null>(edge => {
if (!edge?.node) {
return null;
}
Expand Down
34 changes: 0 additions & 34 deletions src/watchers/twitter/helper.ts

This file was deleted.

34 changes: 20 additions & 14 deletions src/watchers/twitter/index.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import pluralize from "pluralize";
import { TwitterApi, TwitterApiReadOnly } from "twitter-api-v2";
import { TwitterApiRateLimitPlugin } from "@twitter-api-v2/plugin-rate-limit";

import { UserData } from "@repositories/models/user";

import { BaseWatcher } from "@watchers/base";
import { TwitterWatcherOptions } from "@watchers/twitter/types";

import { TwitterHelper } from "@watchers/twitter/helper";

export class TwitterWatcher extends BaseWatcher<"Twitter"> {
private readonly helper: TwitterHelper;
private readonly twitterClient: TwitterApiReadOnly;

public constructor({ bearerToken }: TwitterWatcherOptions) {
super("Twitter");
this.helper = new TwitterHelper(bearerToken);
this.twitterClient = new TwitterApi(bearerToken, { plugins: [new TwitterApiRateLimitPlugin()] }).readOnly;
}

public async initialize() {
await this.helper.initialize();
return;
}
public getProfileUrl(user: UserData) {
return `https://twitter.com/${user.userId}`;
}
public async getFollowers() {
const allFollowers = await this.helper.getFollowers();

this.logger.verbose("Successfully crawled {} {}", [
allFollowers.length,
pluralize("follower", allFollowers.length),
]);
protected async getFollowers() {
const followers = await this.twitterClient.v2.followers("1569485307049033729", {
max_results: 1000,
"user.fields": ["id", "name", "username", "profile_image_url"],
});

return allFollowers;
return followers.data.map(user => ({
uniqueId: user.id,
displayName: user.name,
userId: user.username,
}));
}
}

0 comments on commit fbc278f

Please sign in to comment.