Compare commits

...

1 Commits
master ... qb

Author SHA1 Message Date
5d07426e23
wip 2024-10-06 22:09:08 +09:00
7 changed files with 204 additions and 7 deletions

View File

@ -0,0 +1,104 @@
/**
* API
* @author Xeltica
*/
import { BadRequestError, Body, CurrentUser, Delete, Get, JsonController, NotFoundError, OnUndefined, Param, Post, Put } from 'routing-controllers';
import { IUser } from '../../common/types/user.js';
import { Questions } from '../models/index.js';
import { AnnounceCreate } from './body/announce-create.js';
import { AnnounceUpdate } from './body/announce-update.js';
import { IdProp } from './body/id-prop.js';
@JsonController('/questions')
export class AnnouncementController {
@Get() get() {
const query = Questions.createQueryBuilder('announcement')
.orderBy('"announcement"."createdAt"', 'DESC');
return query.getMany();
}
@OnUndefined(204)
@Post() async post(@CurrentUser({ required: true }) user: IUser, @Body({required: true}) {title, body}: AnnounceCreate) {
if (!user.isAdmin) {
throw new BadRequestError('Not an Admin');
}
if (!title || !body) {
throw new BadRequestError();
}
await Questions.insert({
createdAt: new Date(),
title,
body,
});
}
@OnUndefined(204)
@Put() async update(@CurrentUser({ required: true }) user: IUser, @Body() {id, title, body}: AnnounceUpdate) {
if (!user.isAdmin) {
throw new BadRequestError('Not an Admin');
}
if (!id || !title || !body) {
throw new BadRequestError();
}
if (!(await Questions.findOne(id))) {
throw new NotFoundError();
}
await Questions.update(id, {
title,
body,
});
}
@OnUndefined(204)
@Post('/like/:id') async like(@CurrentUser({ required: true }) user: IUser, @Param('id') id: string) {
if (!user.isAdmin) {
throw new BadRequestError('Not an Admin');
}
const idNumber = Number(id);
if (isNaN(idNumber)) {
throw new NotFoundError();
}
if (!id) {
throw new BadRequestError();
}
const announcement = await Questions.findOne(Number(idNumber));
if (!announcement) {
throw new NotFoundError();
}
await Questions.update(id, {
like: announcement.like + 1,
});
return announcement.like + 1;
}
@Delete() async delete(@CurrentUser({ required: true }) user: IUser, @Body() {id}: IdProp) {
if (!user.isAdmin) {
throw new BadRequestError('Not an Admin');
}
if (!id) {
throw new BadRequestError();
}
await Questions.delete(id);
}
@Get('/:id') async getDetail(@Param('id') id: string) {
const idNumber = Number(id);
if (isNaN(idNumber)) {
throw new NotFoundError();
}
const announcement = await Questions.findOne(idNumber);
if (!announcement) {
throw new NotFoundError();
}
return announcement;
}
}

View File

@ -40,6 +40,7 @@ export class SessionController {
if (setting.notificationIcon !== undefined) s.notificationIcon = setting.notificationIcon;
if (setting.useRanking !== undefined) s.useRanking = setting.useRanking;
if (setting.appendHashtag !== undefined) s.appendHashtag = setting.appendHashtag;
if se
if (Object.keys(s).length === 0) return;
await updateUser(user.username, user.host, s);
}

View File

@ -0,0 +1,71 @@
import { Entity, PrimaryGeneratedColumn, Column, Index, JoinColumn, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { IQuestion } from '../../../common/types/question.js';
@Entity()
export class Question implements IQuestion {
@PrimaryGeneratedColumn()
public id: number;
@Index()
@Column({
type: 'number' as const,
nullable: true,
comment: 'The ID of reply target.',
})
public recipientId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
public recipient: User;
@Index()
@Column({
type: 'number' as const,
nullable: true,
comment: 'The ID of reply target.',
})
public senderId: User['id'] | null;
@ManyToOne(type => User, {
onDelete: 'SET NULL',
})
@JoinColumn()
public sender: User | null;
@Column({
type: 'timestamp without time zone',
})
public createdAt: Date;
@Column({
type: 'varchar',
length: 8192,
})
public question: string;
@Column({
type: 'timestamp without time zone',
})
public repliedAt: Date;
@Column({
type: 'varchar',
length: 8192,
})
public reply: string;
@Column({
type: 'boolean',
default: false,
})
public isDeleted: boolean;
@Column({
type: 'boolean',
default: false,
})
public isNSFW: boolean;
}

View File

@ -1,8 +1,10 @@
import { User } from './entities/user.js';
import { UsedToken } from './entities/used-token.js';
import { getRepository } from 'typeorm';
import { Announcement } from './entities/announcement.js';
import { Question } from './entities/question.js';
import { getRepository } from 'typeorm';
export const Users = getRepository(User);
export const UsedTokens = getRepository(UsedToken);
export const Announcements = getRepository(Announcement);
export const Questions = getRepository(Question);

View File

@ -1,11 +1,17 @@
const allTexts: string[] = [
'아무말 빔----',
'휴대폰용 보험은 가입하셨나요?',
'아, 뭐하려고 했더라...?',
'여기 재밌는 이야기가 있어요! https://youtu.be/dQw4w9WgXcQ',
'모니터를 청소하려고 물을 뿌렸더니, 아무것도 보이지 않게 됐어요...'
];
const getRandomText = () => allTexts[Math.floor(Math.random() * allTexts.length)];
function getRandomText(texts: string[]): string {
if (texts.length < 1) texts = allTexts;
return texts[Math.floor(Math.random() * texts.length)];
}
export const createGacha = () => {
const result = getRandomText();
export function createGacha(texts: string[]): string {
const result = getRandomText(texts);
return result;
};
}

View File

@ -25,7 +25,7 @@ export const variables: Record<string, Variable> = {
username: (_, user) => String(user.username),
host: (_, user) => String(user.host),
rating: (_, user) => String(user.rating),
gacha: () => createGacha(),
gacha: () => createGacha(user.gachaTexts),
};
const variableRegex = /\{([a-zA-Z0-9_]+?)}/g;
@ -47,7 +47,7 @@ export const format = (user: IUser, count: Count): string => {
return !v ? m : typeof v === 'function' ? v(score, user) : v;
});
if (user.appendHashtag) {
result = result + '\n\n#misshaialert';
result = result + '\n\n<small>Powered by #misshaialert</small>';
}
return result;
};

View File

@ -0,0 +1,13 @@
import { IUser } from './user.js';
export interface IQuestion {
id: number;
recipient: IUser;
sender: IUser | null;
createdAt: Date;
question: string;
repliedAt: Date;
reply: string;
isDeleted: boolean;
isNSFW: boolean;
}