Skip to content

Commit 5e4d97e

Browse files
authored
support language change by /lang (#45)
support language change by /lang
2 parents 45b2781 + 137219d commit 5e4d97e

File tree

13 files changed

+191
-66
lines changed

13 files changed

+191
-66
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ RSS is parsered using [rss-parser](https://www.npmjs.com/package/rss-parser)
1919
/export - export subscriptions to opml file
2020
/viewall - view all subscriptions and number of subscribers need to enable in settings
2121
/import - reply this message a opml file to import(in group)
22+
/lang - change language
2223
```
2324

2425
Automatically detecting RSS feed ,you can use `/sub https://www.fengkx.top` rather than `/sub https://www.fengkx.top/atom.xml
@@ -127,6 +128,7 @@ RSS 解析用的是 [rss-parser](https://www.npmjs.com/package/rss-parser),它
127128
/export - 导出订阅到opml文件
128129
/viewall - 查看所有订阅和订阅人数 需要在设置中打开
129130
/import - 回复此消息 opml 文件导入订阅(群组)
131+
/lang - 更改语言
130132
```
131133

132134
自动检测 RSS feed,可以直接 `/sub https://www.fengkx.top` 而不用 `/sub https://www.fengkx.top/atom.xml`

controlers/rss.js

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const twoKeyReply = require('../utils/two-key-reply');
55
const errors = require('../utils/errors');
66

77
ctrl.sub = async (ctx, next) => {
8-
const { feedUrl, chat } = ctx.state;
8+
const { feedUrl, chat, lang } = ctx.state;
99
const feedTitle = ctx.state.feed.title;
1010
const userId = chat.id;
1111
try {
@@ -17,7 +17,7 @@ ctrl.sub = async (ctx, next) => {
1717
);
1818
ctx.state.processMesId = null;
1919
ctx.replyWithMarkdown(`
20-
${i18n['SUB_SUCCESS']}[${ctx.state.feed.title}](${
20+
${i18n[lang]['SUB_SUCCESS']}[${ctx.state.feed.title}](${
2121
ctx.state.feedUrl
2222
})`);
2323
}
@@ -29,7 +29,7 @@ ctrl.sub = async (ctx, next) => {
2929
};
3030

3131
ctrl.unsub = async (ctx, next) => {
32-
const { feedUrl, chat } = ctx.state;
32+
const { feedUrl, chat, lang } = ctx.state;
3333
const userId = chat.id;
3434
try {
3535
const feed = await RSS.getFeedByUrl(feedUrl);
@@ -42,7 +42,7 @@ ctrl.unsub = async (ctx, next) => {
4242
);
4343
ctx.state.processMesId = null;
4444
ctx.replyWithMarkdown(`
45-
${i18n['UNSUB_SUCCESS']}[${feed.feed_title}](${encodeURI(
45+
${i18n[lang]['UNSUB_SUCCESS']}[${feed.feed_title}](${encodeURI(
4646
ctx.state.feedUrl
4747
)})`);
4848
}
@@ -62,13 +62,14 @@ ctrl.rss = async (ctx, next) => {
6262

6363
const userId = ctx.state.chat.id;
6464
const count = await RSS.getSubscribedCountByUserId(userId);
65+
const { lang } = ctx.state;
6566
const kbs = [
6667
{
67-
text: i18n['PAGE_PRE'],
68+
text: i18n[lang]['PAGE_PRE'],
6869
callback_data: 'RSS_' + rawStr + (page - 1)
6970
},
7071
{
71-
text: i18n['PAGE_NEXT'],
72+
text: i18n[lang]['PAGE_NEXT'],
7273
callback_data: 'RSS_' + rawStr + (page + 1)
7374
}
7475
];
@@ -80,7 +81,7 @@ ctrl.rss = async (ctx, next) => {
8081
}
8182
let builder = [];
8283

83-
builder.push(`<strong>${i18n['SUB_LIST']}</strong>`);
84+
builder.push(`<strong>${i18n[lang]['SUB_LIST']}</strong>`);
8485
if (raw) {
8586
feeds.forEach((feed) => {
8687
builder.push(
@@ -101,25 +102,31 @@ ctrl.rss = async (ctx, next) => {
101102

102103
ctrl.unsubAll = async (ctx, next) => {
103104
const userId = ctx.state.chat.id;
105+
const lang = ctx.state.lang;
104106
await RSS.unsubAll(userId);
105-
await ctx.telegram.sendMessage(ctx.chat.id, i18n['UNSUB_ALL_SUCCESS'], {
106-
parse_mode: 'HTML',
107-
disable_web_page_preview: true
108-
});
107+
await ctx.telegram.sendMessage(
108+
ctx.chat.id,
109+
i18n[lang]['UNSUB_ALL_SUCCESS'],
110+
{
111+
parse_mode: 'HTML',
112+
disable_web_page_preview: true
113+
}
114+
);
109115
await next();
110116
};
111117

112118
ctrl.viewAll = async (ctx, next) => {
113119
const limit = 50;
114120
const page = ctx.state.viewallPage || 1;
115121
const count = await RSS.getAllFeedsCount();
122+
const { lang } = ctx.state;
116123
const kbs = [
117124
{
118-
text: i18n['PAGE_PRE'],
125+
text: i18n[lang]['PAGE_PRE'],
119126
callback_data: 'VIEWALL_' + (page - 1)
120127
},
121128
{
122-
text: i18n['PAGE_NEXT'],
129+
text: i18n[lang]['PAGE_NEXT'],
123130
callback_data: 'VIEWALL_' + (page + 1)
124131
}
125132
];
@@ -130,14 +137,14 @@ ctrl.viewAll = async (ctx, next) => {
130137
throw errors.newCtrlErr('NOT_SUB');
131138
}
132139
let builder = [];
133-
builder.push(`<strong>${i18n['ALL_FEED']}</strong>`);
140+
builder.push(`<strong>${i18n[lang]['ALL_FEED']}</strong>`);
134141
feeds.forEach((feed) => {
135142
const url = feed.url.trim();
136143
const title = feed.feed_title.trim();
137144
builder.push(
138-
`<a href="${url}">${title}</a> ${i18n['NUMBER_OF_SUBSCRIBER']}: ${
139-
feed.sub_count
140-
}`
145+
`<a href="${url}">${title}</a> ${
146+
i18n[lang]['NUMBER_OF_SUBSCRIBER']
147+
}: ${feed.sub_count}`
141148
);
142149
});
143150
ctx.state.replyText = builder.join('\n');

database/sql/create_tables.sql

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS "rss_feed"(
77
recent_hash_list TEXT default '[]',
88
error_count INTEGER default 0 not null
99
);
10-
create TABLE IF NOT EXISTS subscribes
10+
CREATE TABLE IF NOT EXISTS subscribes
1111
(
1212
subscribe_id INTEGER
1313
constraint subscribes_pk
@@ -17,3 +17,10 @@ create TABLE IF NOT EXISTS subscribes
1717
references rss_feed(feed_id),
1818
user_id INTEGER not null
1919
);
20+
CREATE TABLE IF NOT EXISTS users
21+
(
22+
user_id INTEGER
23+
constraint users_pk
24+
primary key,
25+
lang TEXT not null
26+
);

i18n/en.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ PAGE_NEXT: Next page
4343
FOUND_FEED_MORE_THAN_ONE: Found feed more than 1
4444
FOUND_FEEDS: Found feeds
4545
FEED_CHANGE_TO: Feed Change to
46+
CHOOSE_LANG: Choose languages
47+
SET_LANG_TO: Set language to
48+
LANG_USAGE: 'USAGE: /lang change language'

i18n/index.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
const yaml = require('js-yaml');
22
const fs = require('fs');
33
const path = require('path');
4-
const config = require('../config');
5-
const yamlPath = path.join(__dirname, `${config.lang}.yaml`);
64

7-
let base = fs.readFileSync(path.join(__dirname, 'en.yaml'), {
8-
encoding: 'utf-8'
9-
});
10-
base = yaml.safeLoad(base);
5+
const i18n = {};
6+
const baseStr = fs.readFileSync(path.join(__dirname, `en.yaml`));
117

12-
const yamlStr = fs.readFileSync(yamlPath, {
13-
encoding: 'utf-8'
14-
});
8+
fs.readdirSync(__dirname)
9+
.filter((i) => i.endsWith('.yaml'))
10+
.map((i) => {
11+
const code = i.substr(0, i.length - 5);
12+
i18n[code] = Object.assign(
13+
yaml.safeLoad(baseStr),
14+
yaml.safeLoad(fs.readFileSync(path.join(__dirname, `${code}.yaml`)))
15+
);
16+
return code;
17+
});
1518

16-
module.exports = Object.assign(base, yaml.safeLoad(yamlStr));
19+
module.exports = i18n;

i18n/zh-cn.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ PAGE_NEXT: 下一页
4343
FOUND_FEED_MORE_THAN_ONE: 找到不止一个订阅地址
4444
FOUND_FEEDS: 找到的订阅地址
4545
FEED_CHANGE_TO: 订阅源地址变为
46+
CHOOSE_LANG: 选择语言
47+
SET_LANG_TO: 更改语言为
48+
LANG_USAGE: 使用方法:/lang 更改语言

index.js

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const send = require('./utils/send');
66
const logger = require('./utils/logger');
77
const errors = require('./utils/errors');
88
const i18n = require('./i18n');
9+
const USERS = require('./proxies/users');
910
const {
1011
token,
1112
view_all,
@@ -29,16 +30,6 @@ const {
2930
} = require('./middlewares');
3031

3132
const twoKeyReply = require('./utils/two-key-reply');
32-
const confirmation = twoKeyReply(i18n['CONFIRM'], [
33-
{
34-
text: i18n['YES'],
35-
callback_data: 'UNSUB_ALL_YES'
36-
},
37-
{
38-
text: i18n['NO'],
39-
callback_data: 'UNSUB_ALL_NO'
40-
}
41-
]);
4233

4334
(async () => {
4435
await initTable();
@@ -60,26 +51,32 @@ bot.telegram.getMe().then((botInfo) => {
6051

6152
bot.command('start', async (ctx) => {
6253
let builder = [];
63-
builder.push(i18n['WELCOME']);
64-
builder.push(i18n['SUB_USAGE']);
65-
builder.push(i18n['UNSUB_USAGE']);
66-
builder.push(i18n['RSS_USAGE']);
67-
builder.push(i18n['SEND_FILE_IMPORT']);
68-
builder.push(i18n['EXPORT']);
69-
builder.push(i18n['USB_ALL_USAGE']);
70-
if (view_all) builder.push(i18n['VIEW_ALL_USAGE']);
54+
const { lang } = ctx.state;
55+
builder.push(i18n[lang]['WELCOME']);
56+
builder.push(i18n[lang]['SUB_USAGE']);
57+
builder.push(i18n[lang]['UNSUB_USAGE']);
58+
builder.push(i18n[lang]['RSS_USAGE']);
59+
builder.push(i18n[lang]['SEND_FILE_IMPORT']);
60+
builder.push(i18n[lang]['EXPORT']);
61+
builder.push(i18n[lang]['USB_ALL_USAGE']);
62+
builder.push(i18n[lang]['LANG_USAGE']);
63+
if (view_all) builder.push(i18n[lang]['VIEW_ALL_USAGE']);
7164
await ctx.replyWithMarkdown(builder.join('\n'));
7265
});
7366

7467
bot.command('help', async (ctx) => {
7568
let builder = [];
76-
builder.push(i18n['SUB_USAGE']);
77-
builder.push(i18n['UNSUB_USAGE']);
78-
builder.push(i18n['RSS_USAGE']);
79-
builder.push(i18n['SEND_FILE_IMPORT']);
80-
builder.push(i18n['EXPORT']);
81-
builder.push(i18n['USB_ALL_USAGE']);
82-
if (view_all) builder.push(i18n['VIEW_ALL_USAGE']);
69+
builder.push(i18n[lang]['SUB_USAGE']);
70+
builder.push(i18n[lang]['UNSUB_USAGE']);
71+
builder.push(i18n[lang]['RSS_USAGE']);
72+
builder.push(i18n[lang]['SEND_FILE_IMPORT']);
73+
builder.push(i18n[lang]['EXPORT']);
74+
builder.push(i18n[lang]['USB_ALL_USAGE']);
75+
builder.push(i18n[lang]['LANG_USAGE']);
76+
if (view_all) builder.push(i18n[lang]['VIEW_ALL_USAGE']);
77+
builder.push(
78+
`[https://github.com/fengkx/NodeRSSBot/blob/master/README.md](https://github.com/fengkx/NodeRSSBot/blob/master/README.md)`
79+
);
8380
await ctx.replyWithMarkdown(builder.join('\n'));
8481
});
8582

@@ -94,7 +91,7 @@ bot.command('rss', sendError, isAdmin, RSS.rss);
9491
bot.command('export', sendError, isAdmin, exportToOpml);
9592

9693
bot.command('import', async (ctx, next) => {
97-
ctx.reply(i18n['IMPORT_USAGE']);
94+
ctx.reply(i18n[lang]['IMPORT_USAGE']);
9895
await next();
9996
});
10097

@@ -116,9 +113,53 @@ bot.command(
116113
sendError,
117114
isAdmin,
118115
// RSS.unsubAll,
119-
confirmation
116+
async (ctx, next) => {
117+
const { lang } = ctx.state;
118+
await twoKeyReply(i18n[lang]['CONFIRM'], [
119+
{
120+
text: i18n[lang]['YES'],
121+
callback_data: 'UNSUB_ALL_YES'
122+
},
123+
{
124+
text: i18n[lang]['NO'],
125+
callback_data: 'UNSUB_ALL_NO'
126+
}
127+
])(ctx, next);
128+
}
120129
);
121130

131+
bot.command('lang', sendError, isAdmin, async (ctx, next) => {
132+
const kbs = Object.keys(i18n).map((i) => {
133+
return {
134+
text: i,
135+
callback_data: `CHANGE_LANG_${i}_${ctx.state.chat.id}`
136+
};
137+
});
138+
await ctx.telegram.sendMessage(
139+
ctx.chat.id,
140+
i18n[ctx.state.lang]['CHOOSE_LANG'],
141+
{
142+
parse_mode: 'HTML',
143+
disable_web_page_preview: true,
144+
reply_markup: {
145+
inline_keyboard: [[...kbs]]
146+
}
147+
}
148+
);
149+
await next();
150+
});
151+
152+
bot.action(/^CHANGE_LANG[\w_]+/, async (ctx, next) => {
153+
const cb = ctx.callbackQuery;
154+
const data = cb.data.split('_');
155+
const lang = data[data.length - 2];
156+
const id = data[data.length - 1];
157+
await USERS.setLangById(id, lang);
158+
ctx.telegram.answerCbQuery(cb.id, i18n[lang]['SET_LANG_TO'] + ' ' + lang);
159+
await ctx.telegram.deleteMessage(cb.message.chat.id, cb.message.message_id);
160+
await next();
161+
});
162+
122163
bot.hears(
123164
/(((https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w\-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\\w]*))?)/gm,
124165
async (ctx, next) => {
@@ -150,7 +191,7 @@ bot.action(
150191

151192
bot.action('UNSUB_ALL_NO', async (ctx, next) => {
152193
const cb = ctx.callbackQuery;
153-
ctx.telegram.answerCbQuery(cb.id, i18n['CANCEL']);
194+
ctx.telegram.answerCbQuery(cb.id, i18n[lang]['CANCEL']);
154195
await ctx.telegram.deleteMessage(cb.message.chat.id, cb.message.message_id);
155196
await next();
156197
});
@@ -214,7 +255,7 @@ function startFetchProcess(restartTime) {
214255
bot,
215256
`${feed.feed_title}: <a href="${feed.url}">${
216257
feed.url
217-
}</a> ${i18n['ERROR_MANY_TIME']} ${err}`,
258+
}</a> ${i18n[lang]['ERROR_MANY_TIME']} ${err}`,
218259
feed
219260
);
220261
}
@@ -223,12 +264,12 @@ function startFetchProcess(restartTime) {
223264
const builder = [];
224265
builder.push(
225266
`${feed.feed_title}: <a href="${feed.url}"></a> ${
226-
i18n['ERROR_MANY_TIME']
267+
i18n[lang]['ERROR_MANY_TIME']
227268
}`
228269
);
229-
builder.push(`<b>${i18n['FOUND_FEEDS']}</b>:`);
270+
builder.push(`<b>${i18n[lang]['FOUND_FEEDS']}</b>:`);
230271
builder.push(...new_feed);
231-
builder.push(`${i18n['FEED_CHANGE_TO']} ${new_feed[0]}`);
272+
builder.push(`${i18n[lang]['FEED_CHANGE_TO']} ${new_feed[0]}`);
232273
send(bot, builder.join('\n'), feed);
233274
}
234275
}

middlewares/import-from-opml.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const getOutlines = function(data) {
2525
};
2626

2727
module.exports = async (ctx, next) => {
28-
const { fileLink } = ctx.state;
28+
const { fileLink, lang } = ctx.state;
2929

3030
try {
3131
const res = await got.get(fileLink);
@@ -47,7 +47,7 @@ module.exports = async (ctx, next) => {
4747
}
4848
})
4949
);
50-
let text = `<strong>${i18n['IMPORT_SUCCESS']}</strong>`;
50+
let text = `<strong>${i18n[lang]['IMPORT_SUCCESS']}</strong>`;
5151
outlines.forEach((outline) => {
5252
text += `\n<a href="${outline.xmlUrl}">${outline.text}</a>`;
5353
});

0 commit comments

Comments
 (0)