UNPKG

ptt-client

Version:

A Node client for fetching data from ptt.cc.

185 lines (165 loc) 4.84 kB
import {ObjectLiteral} from '../../../common/ObjectLiteral'; import {SelectQueryBuilder} from '../../../utils/query-builder/SelectQueryBuilder'; import {keymap as key} from '../../../utils'; import {substrWidth} from '../../../utils/char'; export class Article { boardname: string; id: number; push: string; date: string; timestamp: string; author: string; status: string; title: string; fixed: boolean; private _data: string[] = []; get data(): ReadonlyArray<string> { return this._data; } set data(data: ReadonlyArray<string>) { this._data = data.slice(); } /** * @deprecated */ get lines(): ReadonlyArray<string> { return this.data; } /** * @deprecated */ set lines(data: ReadonlyArray<string>) { this.data = data; } /** * @deprecated */ get sn(): number { return this.id; } constructor() { } static fromLine(line: string): Article { const article = new Article(); article.id = +substrWidth('dbcs', line, 1, 7).trim(); article.push = substrWidth('dbcs', line, 9, 2).trim(); article.date = substrWidth('dbcs', line, 11, 5).trim(); article.author = substrWidth('dbcs', line, 17, 12).trim(); article.status = substrWidth('dbcs', line, 30, 2).trim(); article.title = substrWidth('dbcs', line, 32 ).trim(); article.fixed = substrWidth('dbcs', line, 1, 7).trim().includes('★'); return article; } static select(bot): SelectQueryBuilder<Article> { return new ArticleSelectQueryBuilder(bot); } hasHeader(): boolean { if (this.data.length === 0) { return false; } const authorArea = substrWidth('dbcs', this.data[0], 0, 6).trim(); return authorArea === '作者'; } } export enum WhereType { Boardname = 'boardname', Id = 'id', Push = 'push', Author = 'author', Title = 'title', } export class ArticleSelectQueryBuilder extends SelectQueryBuilder<Article> { private bot; private boardname = ''; private wheres: ObjectLiteral[] = []; private id = 0; constructor(bot) { super(); this.bot = bot; } where(type: WhereType, condition: any): this { switch (type) { case WhereType.Boardname: if (this.boardname !== '') { console.warn(`Cannot call where with type "${type}" multiple times`); } else { this.boardname = condition; } break; case WhereType.Id: this.id = +condition; break; case WhereType.Push: this.wheres.push({ type: 'Z', condition }); break; case WhereType.Author: this.wheres.push({ type: 'a', condition }); break; case WhereType.Title: this.wheres.push({ type: '/', condition }); break; default: throw new Error(`Invalid type: ${type}`); } return this; } getQuery(): string { return this.wheres.map(({ type, condition }) => `${type}${condition}${key.Enter}`).join(); } async get(): Promise<Article[]> { await this.bot.enterBoardByName(this.boardname); const found = await this.bot.send(this.getQuery()); if (!found) { return []; } if (this.id > 0) { const id = Math.max(this.id - 9, 1); await this.bot.send(`${key.End}${key.End}${id}${key.Enter}`); } const articles: Article[] = []; for (let i = 3; i <= 22; i++) { const line = this.bot.getLine(i).str; if (line.trim() === '') { break; } const article = Article.fromLine(line); article.boardname = this.boardname; articles.push(article); } // fix id if (articles.length >= 2 && articles[0].id === 0) { for (let i = 1; i < articles.length; i++) { if (articles[i].id !== 0) { articles[0].id = articles[i].id - i; break; } } } for (let i = 1; i < articles.length; i++) { articles[i].id = articles[i - 1].id + 1; } await this.bot.enterIndex(); return articles.reverse(); } async getOne(): Promise<Article|undefined> { await this.bot.enterBoardByName(this.boardname); const found = await this.bot.send(this.getQuery()); if (!found) { return void 0; } /* TODO: validate id */ await this.bot.send(`${this.id}${key.Enter}${key.Enter}`); const article = new Article(); article.id = this.id; article.boardname = this.boardname; article.data = await this.bot.getLines(); if (article.hasHeader()) { article.author = substrWidth('dbcs', this.bot.getLine(0).str, 7, 50).trim(); article.title = substrWidth('dbcs', this.bot.getLine(1).str, 7 ).trim(); article.timestamp = substrWidth('dbcs', this.bot.getLine(2).str, 7 ).trim(); } await this.bot.enterIndex(); return article; } } export default Article;