Commit 415410b9 authored by nanahira's avatar nanahira

finish

parent d71c92e7
export const defaultTemplateMap = new Map<string, string>();
export const DefaultRollText =
'{{&name}} {{#reason}}因为 {{&reason}} 而{{/reason}}投掷了 {{count}} 个 {{size}} 面骰子,投掷出了 {{result}} 点。\n{{&formula}}={{result}}';
'{{&name}} {{#reason}}因为 {{&reason}} 而{{/reason}}投掷了 {{count}} 个 {{size}} 面骰子,投掷出了 {{result}} 点。{{#formula}}\n{{&formula}}{{/formula}}';
defaultTemplateMap.set('roll', DefaultRollText);
export const TooMuchCountText =
'{{&name}} {{#reason}}因为 {{&reason}} 而{{/reason}}投掷了 {{count}} 个 {{size}} 面骰子。\n骰子滚落了一地,找不到了。';
defaultTemplateMap.set('too_much_count', TooMuchCountText);
export const TooMuchSizeText =
'{{&name}} {{#reason}}因为 {{&reason}} 而{{/reason}}投掷了 {{count}} 个 {{size}} 面骰子。\n丢了个球。丢个球啊!';
export const BadUserText = '非法用户。';
export const defaultTemplateMap = new Map<string, string>();
defaultTemplateMap.set('roll', DefaultRollText);
defaultTemplateMap.set('too_much_count', TooMuchCountText);
defaultTemplateMap.set('too_much_size', TooMuchSizeText);
export const BadUserText =
'您已被禁止使用该骰娘。{{#reason}}原因是: {{&reason}}{{/reason}}';
defaultTemplateMap.set('bad_user', BadUserText);
export const BadNameText = `你的名字 {{&name}} 不合法或者太长啦!`;
defaultTemplateMap.set('bad_name', BadNameText);
export const GroupNameChangedText = `成功修改您在本群的昵称为 {{&name}}。`;
defaultTemplateMap.set('group_name_changed', GroupNameChangedText);
export const GlobalNameChangedText = `成功修改您的昵称为 {{&name}}。`;
defaultTemplateMap.set('global_name_changed', GlobalNameChangedText);
const banReasonText =
'{{#banReason}}\n被封禁: 是\n封禁原因: {{&banReason}}{{/banReason}}';
export const GroupUserProfileText = `{{&displayUsername}}({{&user.id}}) 在群 {{&group.id}} 的资料:\n昵称: {{&displayUsername}}${banReasonText}`;
defaultTemplateMap.set('group_user_profile', GroupUserProfileText);
export const GlobalUserProfileText = `{{&name}}({{id}}) 的资料:\n昵称: {{&name}}${banReasonText}\n\n在过的群:{{#groupProfiles}}\n{{group.id}} -> {{&name}}{{/groupProfiles}}`;
defaultTemplateMap.set('global_user_profile', GlobalUserProfileText);
export const UserNotFoundText = `用户 {{&field}} 不存在。`;
defaultTemplateMap.set('user_not_found', UserNotFoundText);
export const PermissionDeniedText = `你没有权限进行 {{&action}} 。`;
defaultTemplateMap.set('permission_denied', PermissionDeniedText);
export const BadParamsText = `参数不正确。`;
defaultTemplateMap.set('bad_params', BadParamsText);
......@@ -5,9 +5,10 @@ import {
NotFoundException,
} from '@nestjs/common';
import { AppLogger } from './app.logger';
import { Connection } from 'typeorm';
import { Brackets, Connection, In } from 'typeorm';
import { InjectConnection } from '@nestjs/typeorm';
import { User } from './entities/User';
import { Group } from './entities/Group';
import { BotService } from './bot/bot.service';
import { UserPermissions } from './constants';
import { diceConfig, DiceConfig } from './config';
......@@ -17,6 +18,8 @@ import { GroupTemplate } from './entities/GroupTemplate';
import { DefaultTemplate } from './entities/DefaultTemplate';
import { defaultTemplateMap } from './DefaultTemplate';
import Mustache from 'mustache';
import { doc } from 'prettier';
import { GroupUserProfile } from './entities/GroupUserProfile';
export interface RollResult {
name: string;
......@@ -28,6 +31,19 @@ export interface RollResult {
results?: number[];
}
export interface KoishiSessionLike {
userId?: string;
username?: string;
groupId?: string;
}
export interface DatabaseUserData {
banReason: string;
user?: User;
group?: Group;
profile?: GroupUserProfile;
}
@Injectable()
export class AppService {
config: DiceConfig;
......@@ -45,15 +61,37 @@ export class AppService {
rollResult.results = _.range(rollResult.count).map(
() => Math.floor(Math.random() * rollResult.size) + 1,
);
rollResult.formula = rollResult.results.join('+');
rollResult.result = _.sum(rollResult.results);
rollResult.formula =
rollResult.count > 1
? `${rollResult.results.join('+')}=${rollResult.result}`
: null;
}
async findOrCreateGroupUserProfile(user: User, group: Group) {
const query = this.db
.getRepository(GroupUserProfile)
.createQueryBuilder('profile')
.innerJoinAndSelect('profile.user', 'user')
.innerJoinAndSelect('profile.group', 'group')
.where('user.id = :userId', { userId: user.id })
.andWhere('group.id = :groupId', { groupId: group.id });
let profile = await query.getOne();
if (!profile) {
profile = new GroupUserProfile();
profile.user = user;
profile.group = group;
profile.name = user.name;
return await this.db.getRepository(GroupUserProfile).save(profile);
}
return profile;
}
async checkJoinGroup(userId: string) {
const user = await this.botService.findOrCreateUser(userId);
if (user.checkPermissions(UserPermissions.inviteBot)) {
return true;
} else if (user.isBanned) {
} else if (user.banReason) {
return false;
}
return undefined;
......@@ -73,7 +111,7 @@ export class AppService {
.getOne();
}
private async getTemplate(key: string, groupId: string) {
private async getTemplate(key: string, groupId?: string) {
let template: TextTemplate;
if (groupId) {
template = await this.getTemplateForGroup(key, groupId);
......@@ -90,7 +128,7 @@ export class AppService {
return null;
}
private async renderTemplate(key: string, data: any, groupId?: string) {
async renderTemplate(key: string, data: any, groupId?: string) {
const template = await this.getTemplate(key, groupId);
if (template) {
return template.render(data);
......@@ -100,60 +138,91 @@ export class AppService {
async isUserHasPermissions(userId: string, username: string, perm: number) {
const user = await this.botService.findOrCreateUser(userId, username);
if (user.isBanned) {
if (user.banReason) {
return false;
}
return user.checkPermissions(perm);
}
private async checkUserAndGroup(
userId: string,
username: string,
groupId: string,
) {
const user = await this.botService.findOrCreateUser(userId, username);
if (user.isBanned) {
return false;
private async getDatabaseUserData(
userData: KoishiSessionLike,
): Promise<DatabaseUserData> {
let user: User = null;
if (userData.userId) {
user = await this.botService.findOrCreateUser(
userData.userId,
userData.username,
);
if (user.banReason) {
return { banReason: user.banReason };
}
}
const group = await this.botService.findOrCreateGroup(groupId);
if (group.isBanned) {
return false;
let group: Group = null;
if (userData.groupId) {
group = await this.botService.findOrCreateGroup(userData.groupId);
if (group.banReason) {
return { banReason: group.banReason };
}
}
return true;
let profile: GroupUserProfile = null;
if (user && group) {
profile = await this.findOrCreateGroupUserProfile(user, group);
if (profile.banReason) {
return { banReason: profile.banReason };
}
}
return { user, group, profile, banReason: null };
}
async rollDice(
rollResult: RollResult,
userId: string,
groupId: string,
userData: KoishiSessionLike,
): Promise<string> {
if (!(await this.checkUserAndGroup(userId, rollResult.name, groupId))) {
return await this.renderTemplate('bad_user', {});
const { user, group, profile, banReason } = await this.getDatabaseUserData(
userData,
);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
if (rollResult.count > this.config.maxDiceCount) {
return await this.renderTemplate('too_much_count', rollResult, groupId);
return await this.renderTemplate(
'too_much_count',
rollResult,
userData.groupId,
);
}
if (rollResult.size > this.config.maxDiceSize) {
return await this.renderTemplate('too_much_size', rollResult, groupId);
return await this.renderTemplate(
'too_much_size',
rollResult,
userData.groupId,
);
}
rollResult.name = profile
? profile.getDisplayUsername(userData.username)
: user.name;
AppService.rollProcess(rollResult);
return await this.renderTemplate('roll', rollResult, groupId);
return await this.renderTemplate('roll', rollResult, userData.groupId);
}
async getGroupTemplate(key: string, groupId: string) {
const group = await this.botService.findOrCreateGroup(groupId);
if (group.isBanned) {
return await this.renderTemplate('bad_user', {});
async getGroupTemplate(userData: KoishiSessionLike, key: string) {
const { user, group, profile, banReason } = await this.getDatabaseUserData(
userData,
);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
const template = await this.getTemplate(key, groupId);
const template = await this.getTemplate(key, userData.groupId);
if (template) {
return template.display();
}
return `${key} => ${defaultTemplateMap.get(key) || '没有这个模板'}`;
}
async getAllGroupTemplates(groupId: string) {
const group = await this.botService.findOrCreateGroup(groupId);
if (group.isBanned) {
return await this.renderTemplate('bad_user', {});
async getAllGroupTemplates(userData: KoishiSessionLike) {
const { user, group, profile, banReason } = await this.getDatabaseUserData(
userData,
);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
const notSetTemplateNames = Array.from(defaultTemplateMap.keys()).filter(
(tName) => !group.templates.find((t) => t.key === tName),
......@@ -164,15 +233,21 @@ export class AppService {
'\n',
)}`;
}
async setGroupTemplate(key: string, groupId: string, content: string) {
const group = await this.botService.findOrCreateGroup(groupId);
if (group.isBanned) {
return await this.renderTemplate('bad_user', {});
async setGroupTemplate(
userData: KoishiSessionLike,
key: string,
content: string,
) {
const { user, group, profile, banReason } = await this.getDatabaseUserData(
userData,
);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
if (!defaultTemplateMap.has(key)) {
return `模板 ${key} 不存在。`;
}
let template = await this.getTemplateForGroup(key, groupId);
let template = await this.getTemplateForGroup(key, userData.groupId);
if (!template) {
template = new GroupTemplate();
template.key = key;
......@@ -182,16 +257,192 @@ export class AppService {
await this.db.getRepository(GroupTemplate).save(template);
return `成功设置自定义模板: ${template.display()}`;
}
async clearGroupTemplate(key: string, groupId: string) {
const group = await this.botService.findOrCreateGroup(groupId);
if (group.isBanned) {
return await this.renderTemplate('bad_user', {});
async clearGroupTemplate(userData: KoishiSessionLike, key: string) {
const { user, group, profile, banReason } = await this.getDatabaseUserData(
userData,
);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
const template = await this.getTemplateForGroup(key, groupId);
const template = await this.getTemplateForGroup(key, userData.groupId);
if (!template) {
return `自定义模板 ${key} 没有设置过。`;
}
await this.db.getRepository(GroupTemplate).delete(template);
return `成功清除模板 ${key}`;
}
async getGlobalTemplate(userData: KoishiSessionLike, key: string) {
const { user, banReason } = await this.getDatabaseUserData(userData);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
const template = await this.getTemplate(key);
if (template) {
return template.display();
}
return `${key} => ${defaultTemplateMap.get(key) || '没有这个模板'}`;
}
async getAllGlobalTemplates(userData: KoishiSessionLike) {
const { user, banReason } = await this.getDatabaseUserData(userData);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
const allTemplatesList = Array.from(defaultTemplateMap.keys());
const templates = await this.db
.getRepository(DefaultTemplate)
.find({ where: { key: In(allTemplatesList) } });
const notSetTemplateNames = Array.from(defaultTemplateMap.keys()).filter(
(tName) => !templates.find((t) => t.key === tName),
);
return `设置过的默认模板有:\n${templates
.map((t) => t.display())
.join('\n')}\n\n还没有设置的默认模板有:\n${notSetTemplateNames.join(
'\n',
)}`;
}
async setGlobalTemplate(
userData: KoishiSessionLike,
key: string,
content: string,
) {
const { user, banReason } = await this.getDatabaseUserData(userData);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
if (!defaultTemplateMap.has(key)) {
return `模板 ${key} 不存在。`;
}
let template = await this.getTemplateForGroup(key, userData.groupId);
if (!template) {
template = new GroupTemplate();
template.key = key;
}
template.changeContent(content);
await this.db.getRepository(DefaultTemplate).save(template);
return `成功设置默认模板: ${template.display()}`;
}
async clearGlobalTemplate(userData: KoishiSessionLike, key: string) {
const { user, banReason } = await this.getDatabaseUserData(userData);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
const template = await this.db
.getRepository(DefaultTemplate)
.findOne({ where: { key } });
if (!template) {
return `默认模板 ${key} 没有设置过。`;
}
await this.db.getRepository(DefaultTemplate).delete(template);
return `成功清除模板 ${key}`;
}
async setGroupUsername(userData: KoishiSessionLike, name: string) {
const { user, group, profile, banReason } = await this.getDatabaseUserData(
userData,
);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
if (!name || name.length > 32) {
return await this.renderTemplate('bad_name', { name }, userData.groupId);
}
profile.name = name;
await this.db.getRepository(GroupUserProfile).save(profile);
return await this.renderTemplate(
'group_name_changed',
{ name },
userData.groupId,
);
}
async setGlobalUsername(userData: KoishiSessionLike, name: string) {
const { user, banReason } = await this.getDatabaseUserData(userData);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
if (!name || name.length > 32) {
return await this.renderTemplate('bad_name', { name });
}
user.name = name;
await this.db.getRepository(User).save(user);
return await this.renderTemplate('global_name_changed', { name });
}
async getGroupUserProfile(userData: KoishiSessionLike, field?: string) {
const { user, group, profile, banReason } = await this.getDatabaseUserData(
userData,
);
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
let targetProfiles = [profile];
if (field) {
targetProfiles = await this.db
.getRepository(GroupUserProfile)
.createQueryBuilder('profile')
.innerJoinAndSelect('profile.user', 'user')
.innerJoinAndSelect('profile.group', 'group')
.where('group.id = :groupId')
.andWhere(
new Brackets((qb) => {
qb.where('profile.name = :field')
.orWhere('user.id = :field')
.orWhere('user.name = :field');
}),
)
.setParameters({ groupId: group.id, field })
.getMany();
if (!targetProfiles) {
return await this.renderTemplate(
'user_not_found',
{ field },
userData.groupId,
);
}
}
return (
await Promise.all(
targetProfiles.map((targetProfile) =>
this.renderTemplate(
'group_user_profile',
targetProfile.toDscriptionObject(),
userData.groupId,
),
),
)
).join('\n----------\n');
}
async getGlobalUserProfile(userData: KoishiSessionLike, field?: string) {
const { user, banReason } = await this.getDatabaseUserData(userData);
let targetUsers = [user];
if (field) {
if (banReason) {
return await this.renderTemplate('bad_user', { reason: banReason });
}
targetUsers = await this.db
.getRepository(User)
.createQueryBuilder('user')
.leftJoinAndSelect('user.groupProfiles', 'profile')
.leftJoinAndSelect('profile.group', 'group')
.where('user.id = :field')
.orWhere('user.name = :field')
.setParameters({ field })
.getMany();
if (!targetUsers) {
return await this.renderTemplate(
'user_not_found',
{ field },
userData.groupId,
);
}
}
return (
await Promise.all(
targetUsers.map((targetUser) =>
this.renderTemplate(
'global_user_profile',
targetUser.toDscriptionObject(),
userData.groupId,
),
),
)
).join('\n----------\n');
}
}
......@@ -43,6 +43,8 @@ export class BotController {
return msg;
});
const groupCtx = this.bot.group(); // change here
const privateCtx = this.bot.private();
const globalCtx = this.bot.all();
groupCtx.middleware(async (session, next) => {
const content = session.content.trim();
const rollResult: RollResult = {
......@@ -55,23 +57,17 @@ export class BotController {
rollResult.count = parseInt(rollMatch[1]) || 1;
rollResult.size = parseInt(rollMatch[2]) || 6;
rollResult.reason = rollMatch[3] ? rollMatch[3].trim() : null;
await session.send(
await this.appService.rollDice(
rollResult,
session.userId,
session.groupId,
),
);
await session.send(await this.appService.rollDice(rollResult, session));
}
return next();
});
groupCtx
globalCtx
.command('rolldice', '投掷骰子')
.option('count', '-c <count:posint> 骰子数量', { fallback: 1 })
.option('size', '-s <count:posint> 骰子面数', { fallback: 6 })
.option('reason', '-r <reason:text> 骰子说明')
.alias('rd', 'roll')
.usage('也支持 .rd<> 和 .r<count>d<size> [reason] 这样的传统语法。')
.usage('也支持 .rd<size> 和 .r<count>d<size> [reason] 这样的传统语法。')
.example('.rolldice -c 2 -s 6 -r "行动判定"')
.action(async (argv, args) => {
const session = argv.session;
......@@ -82,43 +78,43 @@ export class BotController {
reason: null,
...argv.options,
};
return await this.appService.rollDice(
rollResult,
session.userId,
session.groupId,
);
return await this.appService.rollDice(rollResult, session);
});
groupCtx
.command('group.template.get [key:string]', '获取本群自定义模板')
const groupCommand = groupCtx.command('group', '群内指令');
const groupTemplateCommand = groupCommand
.subcommand('.template', '获取本群自定义模板')
.usage(
`只有群管理员才能使用。key 为模板名。目前支持的模板有: ${Array.from(
defaultTemplateMap.keys(),
).join(' ')}`,
)
.example('.group.template.get roll 获取投掷骰子的消息模板。')
.example('.group.template roll 获取投掷骰子的消息模板。')
.action(async (argv, key) => {
const session = argv.session;
if (
!(await this.checkGroupAdminOrPermission(
session,
UserPermissions.TemplateRead,
UserPermissions.GroupTemplateRead,
))
) {
return `${segment('at', { id: session.userId })} 你没有权限哦。`;
return `${segment('at', {
id: session.userId,
})} ${await this.appService.renderTemplate(
'permission_denied',
{ action: '获取本群自定义模板' },
session.groupId,
)}`;
}
let ret;
if (key) {
ret = await this.appService.getGroupTemplate(key, session.groupId);
ret = await this.appService.getGroupTemplate(session, key);
} else {
ret = await this.appService.getAllGroupTemplates(session.groupId);
ret = await this.appService.getAllGroupTemplates(session);
}
return `${segment('at', { id: session.userId })} ${ret}`;
});
groupCtx
.command(
'group.template.set <key:string> <content:text>',
'设置本群自定义模板',
)
groupTemplateCommand
.subcommand('.set <key:string> <content:text>', '设置本群自定义模板')
.usage(
`只有群管理员才能使用。key 为模板名,content为模板内容。目前支持的模板有: ${Array.from(
defaultTemplateMap.keys(),
......@@ -130,23 +126,35 @@ export class BotController {
if (
!(await this.checkGroupAdminOrPermission(
session,
UserPermissions.TemplateWrite,
UserPermissions.GroupTemplateWrite,
))
) {
return `${segment('at', { id: session.userId })} 你没有权限哦。`;
return `${segment('at', {
id: session.userId,
})} ${await this.appService.renderTemplate(
'permission_denied',
{ action: '设置本群自定义模板' },
session.groupId,
)}`;
}
if (!key || !content) {
return `${segment('at', { id: session.userId })} 缺少参数。`;
return `${segment('at', {
id: session.userId,
})} ${await this.appService.renderTemplate(
'bad_params',
{},
session.groupId,
)}`;
}
const ret = await this.appService.setGroupTemplate(
session,
key,
session.groupId,
content,
);
return `${segment('at', { id: session.userId })} ${ret}`;
});
groupCtx
.command('group.template.clear <key:string>', '清除本群自定义模板')
groupTemplateCommand
.subcommand('.clear <key:string>', '清除本群自定义模板')
.usage(
`只有群管理员才能使用。key 为模板名。目前支持的模板有: ${Array.from(
defaultTemplateMap.keys(),
......@@ -158,19 +166,200 @@ export class BotController {
if (
!(await this.checkGroupAdminOrPermission(
session,
UserPermissions.TemplateWrite,
UserPermissions.GroupTemplateWrite,
))
) {
return `${segment('at', { id: session.userId })} 你没有权限哦。`;
return `${segment('at', {
id: session.userId,
})} ${await this.appService.renderTemplate(
'permission_denied',
{ action: '清除本群自定义模板' },
session.groupId,
)}`;
}
if (!key) {
return `${segment('at', { id: session.userId })} 缺少参数。`;
return `${segment('at', {
id: session.userId,
})} ${await this.appService.renderTemplate(
'bad_params',
{},
session.groupId,
)}`;
}
const ret = await this.appService.clearGroupTemplate(session, key);
return `${segment('at', { id: session.userId })} ${ret}`;
});
const groupUserCommand = groupCommand
.subcommand('.user [field:text]', '查看群内用户信息')
.usage('带参数查看其他人的用户信息,但是只有管理员可以用。')
.example(
'.user 查看自己的用户信息。.user Nanahira 查看 Nanahira 的用户信息。',
)
.action(async (argv, field) => {
const session = argv.session;
if (
field &&
!(await this.checkGroupAdminOrPermission(
session,
UserPermissions.GroupUserRead,
))
) {
return `${segment('at', {
id: session.userId,
})} ${await this.appService.renderTemplate(
'permission_denied',
{ action: '查看群员的用户信息' },
session.groupId,
)}`;
}
const ret = await this.appService.getGroupUserProfile(session, field);
return `${segment('at', { id: session.userId })} ${ret}`;
});
groupUserCommand
.subcommand('.name <name:string>', '修改群内用户昵称')
.example('.group.user.name Nanahira')
.action(async (argv, name) => {
const session = argv.session;
if (!name) {
const ret = await this.appService.getGroupUserProfile(session);
return `${segment('at', { id: session.userId })} ${ret}`;
}
const ret = await this.appService.setGroupUsername(session, name);
return `${segment('at', { id: session.userId })} ${ret}`;
});
const userCommand = globalCtx
.command('account [field:text]', '查看用户信息')
.usage('带参数查看其他人的用户信息,但是只有管理员可以用。')
.example(
'.account 查看自己的用户信息。.account Nanahira 查看 Nanahira 的用户信息。',
)
.action(async (argv, field) => {
const session = argv.session;
if (
field &&
!(await this.checkUserPermission(session, UserPermissions.UserRead))
) {
return await this.appService.renderTemplate(
'permission_denied',
{ action: '查看用户信息' },
session.groupId,
);
}
const ret = await this.appService.getGlobalUserProfile(session, field);
return ret;
});
userCommand
.subcommand('.name <name:string>', '修改用户昵称')
.example('.account.name Nanahira 修改用户昵称为 Nanahira')
.action(async (argv, name) => {
const session = argv.session;
if (!name) {
const ret = await this.appService.getGlobalUserProfile(session);
return ret;
}
const ret = await this.appService.setGlobalUsername(session, name);
return ret;
});
const adminCommand = globalCtx
.command('admin', '管理接口')
.usage('这里的命令只有管理员可以用。');
const adminTemplateCommand = adminCommand
.subcommand('.template', '获取默认模板')
.usage(
`只有群管理员才能使用。key 为模板名。目前支持的模板有: ${Array.from(
defaultTemplateMap.keys(),
).join(' ')}`,
)
.example('.admin.template roll 获取投掷骰子的消息模板。')
.action(async (argv, key) => {
const session = argv.session;
if (
!(await this.checkUserPermission(
session,
UserPermissions.TemplateRead,
))
) {
return await this.appService.renderTemplate(
'permission_denied',
{ action: '获取默认模板' },
session.groupId,
);
}
let ret;
if (key) {
ret = await this.appService.getGlobalTemplate(session, key);
} else {
ret = await this.appService.getAllGlobalTemplates(session);
}
return ret;
});
adminTemplateCommand
.subcommand('.set <key:string> <content:text>', '设置默认自定义模板')
.usage(
`只有管理员才能使用。key 为模板名,content为模板内容。目前支持的模板有: ${Array.from(
defaultTemplateMap.keys(),
).join(' ')}`,
)
.example('.admin.template.set roll <内容> 设置投掷骰子的消息模板。')
.action(async (argv, key, content) => {
const session = argv.session;
if (
!(await this.checkUserPermission(
session,
UserPermissions.TemplateWrite,
))
) {
return await this.appService.renderTemplate(
'permission_denied',
{ action: '设置默认自定义模板' },
session.groupId,
);
}
if (!key || !content) {
return await this.appService.renderTemplate(
'bad_params',
{},
session.groupId,
);
}
const ret = await this.appService.clearGroupTemplate(
const ret = await this.appService.setGlobalTemplate(
session,
key,
session.groupId,
content,
);
return `${segment('at', { id: session.userId })} ${ret}`;
return ret;
});
adminTemplateCommand
.subcommand('.clear <key:string>', '清除默认自定义模板')
.usage(
`只有群管理员才能使用。key 为模板名。目前支持的模板有: ${Array.from(
defaultTemplateMap.keys(),
).join(' ')}`,
)
.example('.admin.template.set roll <内容> 设置投掷骰子的消息模板。')
.action(async (argv, key) => {
const session = argv.session;
if (
!(await this.checkUserPermission(
session,
UserPermissions.TemplateWrite,
))
) {
return await this.appService.renderTemplate(
'permission_denied',
{ action: '清除默认自定义模板' },
session.groupId,
);
}
if (!key) {
return await this.appService.renderTemplate(
'bad_params',
{},
session.groupId,
);
}
const ret = await this.appService.clearGlobalTemplate(session, key);
return ret;
});
}
async checkGroupAdminOrPermission(
......@@ -191,11 +380,29 @@ export class BotController {
) {
return (
(await this.checkGroupAdmin(session)) ||
(await this.appService.isUserHasPermissions(
session.userId,
session.username,
UserPermissions.TemplateRead,
))
(await this.checkUserPermission(session, perm))
);
}
async checkUserPermission(
session: Session<
never,
never,
'onebot',
keyof Session.Events,
| keyof Session.MessageType
| 'role'
| 'ban'
| keyof Session.GroupMemberChangeType
| 'poke'
| 'lucky-king'
| 'honor'
>,
perm: number,
) {
return await this.appService.isUserHasPermissions(
session.userId,
session.username,
perm,
);
}
async checkGroupAdmin(
......
......@@ -17,7 +17,10 @@ export class BotService {
async findOrCreateUser(id: string, name?: string) {
const repo = this.db.getRepository(User);
let ent = await repo.findOne({ where: { id } });
let ent = await repo.findOne({
where: { id },
relations: ['groupProfiles', 'groupProfiles.group'],
});
if (ent) {
if (!ent.name && name) {
ent.name = name;
......@@ -39,7 +42,10 @@ export class BotService {
}
async findOrCreateGroup(id: string) {
const repo = this.db.getRepository(Group);
let ent = await repo.findOne({ where: { id }, relations: ['templates'] });
let ent = await repo.findOne({
where: { id },
relations: ['templates', 'userProfiles', 'userProfiles.user'],
});
if (ent) {
return ent;
}
......
......@@ -3,6 +3,7 @@ import { User } from './entities/User';
import { Group } from './entities/Group';
import { DefaultTemplate } from './entities/DefaultTemplate';
import { GroupTemplate } from './entities/GroupTemplate';
import { GroupUserProfile } from './entities/GroupUserProfile';
export function dbConfig() {
return {
......@@ -17,7 +18,7 @@ export function typeormConfig(): TypeOrmModuleOptions {
return {
name: 'app',
type: 'mysql',
entities: [User, Group, DefaultTemplate, GroupTemplate], // entities here
entities: [User, Group, DefaultTemplate, GroupTemplate, GroupUserProfile], // entities here
synchronize: true,
...dbConfig(),
};
......
export const UserPermissions = {
// read
UserRead: 0x1,
GroupRead: 0x2,
GroupUserRead: 0x2,
TemplateRead: 0x4,
GroupTemplateRead: 0x8,
// write
UserWrite: 0x100,
GroupWrite: 0x200,
GroupUserWrite: 0x200,
TemplateWrite: 0x400,
GroupTemplateWrite: 0x800,
// others
inviteBot: 0x10000,
};
import { QQIDBase } from './QQIDBase';
import { Column, Entity, OneToMany } from 'typeorm';
import { GroupTemplate } from './GroupTemplate';
import { GroupUserProfile } from './GroupUserProfile';
@Entity()
export class Group extends QQIDBase {
@OneToMany((type) => GroupTemplate, (template) => template.group)
templates: GroupTemplate[];
@OneToMany((type) => GroupUserProfile, (profile) => profile.group)
userProfiles: GroupUserProfile[];
renderText(key: string, data: any) {
const template = this.templates.find((t) => key === t.key);
if (this.templates) {
......
......@@ -20,7 +20,4 @@ export class GroupTemplate extends TextTemplate {
@ManyToOne((type) => Group, (group) => group.templates)
group: Group;
@RelationId((template: GroupTemplate) => template.group)
groupId: string;
}
import { TimeBase } from './TimeBase';
import {
Column,
Entity,
Index,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { User } from './User';
import { Group } from './Group';
@Entity()
export class GroupUserProfile extends TimeBase {
@PrimaryGeneratedColumn('uuid')
id: string;
@ManyToOne((type) => User, (user) => user.groupProfiles)
user: User;
@ManyToOne((type) => Group, (group) => group.userProfiles)
group: Group;
@Index()
@Column('varchar', { nullable: true, length: 32 })
name: string;
@Column('varchar', { nullable: true, length: 100 })
banReason: string;
getDisplayUsername(fallback?: string) {
return this.name || this.user.name || fallback;
}
toDscriptionObject() {
return {
displayUsername: this.getDisplayUsername(),
...this,
};
}
}
......@@ -5,6 +5,6 @@ export class QQIDBase extends TimeBase {
@PrimaryColumn('varchar', { length: 12 })
id: string;
@Column('tinyint', { default: 0 })
isBanned: number;
@Column('varchar', { nullable: true, length: 100 })
banReason: string;
}
......@@ -9,11 +9,13 @@ export class TextTemplate extends TimeBase {
content: string;
changeContent(content: string) {
this.content = Buffer.from(decode(content), 'utf-8').toString('base64');
//this.content = Buffer.from(decode(content), 'utf-8').toString('base64');
this.content = decode(content);
}
getContent() {
return Buffer.from(this.content, 'base64').toString('utf-8');
//return Buffer.from(this.content, 'base64').toString('utf-8');
return this.content;
}
render(data: any) {
......
import { QQIDBase } from './QQIDBase';
import { Column, Entity, Index } from 'typeorm';
import { Column, Entity, Index, OneToMany } from 'typeorm';
import { GroupUserProfile } from './GroupUserProfile';
@Entity()
export class User extends QQIDBase {
......@@ -10,7 +11,14 @@ export class User extends QQIDBase {
@Column('int', { default: 0, unsigned: true }) // default with all read permissions
permissions: number;
@OneToMany((type) => GroupUserProfile, (profile) => profile.user)
groupProfiles: GroupUserProfile[];
checkPermissions(permissionNeeded: number) {
return !!(this.permissions & permissionNeeded);
}
toDscriptionObject() {
return this;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment