add API comments
This commit is contained in:
parent
fe34c8a91c
commit
d62bf01b4a
7 changed files with 85 additions and 0 deletions
16
src/bot.ts
16
src/bot.ts
|
@ -12,6 +12,9 @@ import {
|
|||
import { channel, log_from, SimpleResult, split_promise } from "./utils.ts";
|
||||
const log = (...args: unknown[]) => log_from(import.meta.url, ...args);
|
||||
|
||||
/**
|
||||
* Wraps a discord bot and implements required actions.
|
||||
*/
|
||||
export class EpitlsBot {
|
||||
private bot;
|
||||
private token;
|
||||
|
@ -29,6 +32,10 @@ export class EpitlsBot {
|
|||
>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to discord API server and registers slash commands.\
|
||||
* Needs to be run after construction as it is an asynchronous operation.
|
||||
*/
|
||||
public async start() {
|
||||
const { promise, resolver } = split_promise<void>();
|
||||
this.bot.on("ready", () => resolver());
|
||||
|
@ -37,6 +44,9 @@ export class EpitlsBot {
|
|||
await this.register_commands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a discord role to a discord user.
|
||||
*/
|
||||
public async assign_role(user_id: string, guild_id: string, role_id: string) {
|
||||
const guild = await this.bot.guilds.fetch(guild_id);
|
||||
const member = await guild.members.fetch({ user: user_id });
|
||||
|
@ -46,10 +56,16 @@ export class EpitlsBot {
|
|||
log(`Assigned role '${role.name}' to user '${member.displayName}'.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull-style API to wait for and receive account association requests.
|
||||
*/
|
||||
public async *receive_associations() {
|
||||
while (true) yield await this.assoc_channel.receive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connected bot name accessor.
|
||||
*/
|
||||
public bot_name() {
|
||||
return this.bot.user?.displayName;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts";
|
||||
|
||||
/**
|
||||
* Wraps the CRI API.
|
||||
*/
|
||||
export class CriApi {
|
||||
private token;
|
||||
|
||||
|
@ -7,6 +10,9 @@ export class CriApi {
|
|||
this.token = token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an array of the groups an user has been part of.
|
||||
*/
|
||||
public async groups_of(login: string) {
|
||||
const response = await fetch(`https://cri.epita.fr/api/v2/users/${login}/`, {
|
||||
headers: {
|
||||
|
@ -32,6 +38,9 @@ export class CriApi {
|
|||
return Array.from(result.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests wether a given login exists within the CRI registry.
|
||||
*/
|
||||
public async user_exists(login: string) {
|
||||
const response = await fetch(`https://cri.epita.fr/api/v2/users/${login}/`, {
|
||||
headers: {
|
||||
|
|
|
@ -7,6 +7,9 @@ export type EmailerConfig = {
|
|||
password: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps emailing process.
|
||||
*/
|
||||
export class Emailer {
|
||||
private client;
|
||||
private config;
|
||||
|
|
|
@ -22,6 +22,9 @@ async function main() {
|
|||
await service.serve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Context of the service.
|
||||
*/
|
||||
class Service {
|
||||
state;
|
||||
bot;
|
||||
|
@ -35,6 +38,9 @@ class Service {
|
|||
this.rules = rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main loops.
|
||||
*/
|
||||
async serve() {
|
||||
await this.update_all_users_roles();
|
||||
|
||||
|
|
12
src/rules.ts
12
src/rules.ts
|
@ -1,6 +1,9 @@
|
|||
export type TargetRole = { guild_id: string; role_id: string };
|
||||
type SerializedRule = { group_id: string; target_role: TargetRole };
|
||||
|
||||
/**
|
||||
* A set of rules for associating CRI groups with Discord roles.
|
||||
*/
|
||||
export class RuleSet {
|
||||
private rules;
|
||||
|
||||
|
@ -8,6 +11,9 @@ export class RuleSet {
|
|||
this.rules = new Map<string, TargetRole[]>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a RuleSet from a JSON serialized file.
|
||||
*/
|
||||
public static async from_file(path: string) {
|
||||
const result = new RuleSet();
|
||||
const file_content = await Deno.readTextFile(path);
|
||||
|
@ -16,10 +22,16 @@ export class RuleSet {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets which Discord roles must be assigned to a user which is part of a given CRI group.
|
||||
*/
|
||||
public roles_for_group(group_id: string) {
|
||||
return this.rules.get(group_id) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of managed Discord roles.
|
||||
*/
|
||||
public size() {
|
||||
let result = 0;
|
||||
for (const roles of this.rules.values()) result += roles.length;
|
||||
|
|
23
src/state.ts
23
src/state.ts
|
@ -1,29 +1,49 @@
|
|||
import { v1 as uuid } from "https://deno.land/std@0.213.0/uuid/mod.ts";
|
||||
|
||||
export type StoredUser = { discord_user_id: string; cri_login: string };
|
||||
/**
|
||||
* Wraps the persistent state, containing user associaitons.
|
||||
*/
|
||||
export class State {
|
||||
/**
|
||||
* note : We are using a Deno.Kv as storage.\
|
||||
* Its API is comparable to a Key-Value database.
|
||||
*/
|
||||
kv;
|
||||
|
||||
constructor(kv: Deno.Kv) {
|
||||
this.kv = kv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a persistent State that is stored in the specified directory.
|
||||
*/
|
||||
public static async from_dir(local_path: string) {
|
||||
await Deno.mkdir(local_path, { recursive: true });
|
||||
const kv = await Deno.openKv(local_path + "/kv");
|
||||
return new State(kv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates all stored users associations.
|
||||
*/
|
||||
public async *users() {
|
||||
const query = this.kv.list({ prefix: ["users/"] });
|
||||
for await (const { value } of query) yield value as StoredUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count stored users associations.
|
||||
*/
|
||||
public async users_count() {
|
||||
let result = 0;
|
||||
for await (const _ of this.users()) result += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the association of a user given its discord user id.
|
||||
*/
|
||||
public async get_user(discord_user_id: string) {
|
||||
const { value: user_uuid } = await this.kv.get(["users_by_discord_id/", discord_user_id]);
|
||||
if (user_uuid === null) return undefined;
|
||||
|
@ -31,6 +51,9 @@ export class State {
|
|||
return stored as StoredUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a new user association.
|
||||
*/
|
||||
public async set_user(discord_user_id: string, cri_login: string) {
|
||||
await this.remove_user(discord_user_id);
|
||||
await this.add_user(discord_user_id, cri_login);
|
||||
|
|
16
src/utils.ts
16
src/utils.ts
|
@ -1,11 +1,18 @@
|
|||
import * as path from "https://deno.land/std@0.213.0/path/mod.ts";
|
||||
import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts";
|
||||
|
||||
/**
|
||||
* Gets the root path of the clonned project.
|
||||
*/
|
||||
export function root_path() {
|
||||
const this_path = new URL(import.meta.url).pathname;
|
||||
return path.resolve(this_path, "..", "..");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates handles to a channel.\
|
||||
* c.f; https://en.wikipedia.org/wiki/Channel_(programming)
|
||||
*/
|
||||
export function channel<T>() {
|
||||
const inner = {
|
||||
items: [] as T[],
|
||||
|
@ -29,6 +36,9 @@ export function channel<T>() {
|
|||
|
||||
const resolves_to = <T>(item: T) => new Promise<T>((r) => r(item));
|
||||
|
||||
/**
|
||||
* Returns both a promise and its resolver.
|
||||
*/
|
||||
export function split_promise<T>() {
|
||||
let resolver: null | ((item: T) => void) = null;
|
||||
const promise = new Promise<T>((r) => resolver = r);
|
||||
|
@ -36,12 +46,18 @@ export function split_promise<T>() {
|
|||
return { promise, resolver };
|
||||
}
|
||||
|
||||
/**
|
||||
* Logging function factory.
|
||||
*/
|
||||
export function log_from(url: string, ...args: unknown[]) {
|
||||
const date = new Date().toLocaleString("fr-FR");
|
||||
const file = path.basename(new URL(url).pathname);
|
||||
console.log(`[${date}][epitls][${file}]`, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and parse a configuration file containing the current instance' secrets.
|
||||
*/
|
||||
export async function read_secrets(path: string) {
|
||||
const content = await Deno.readTextFile(path);
|
||||
return z.object({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue