Skip to content
This repository has been archived by the owner on Aug 9, 2023. It is now read-only.

Commit

Permalink
Feature group support (#20)
Browse files Browse the repository at this point in the history
* adds rate limiting for groups

To enable a realistic use in group chats, I added rate limiting with an initial limit of 4 messages per 12 seconds per group.

* adds support for group join message and /admins command.

+ Fancy info message when adding the bot to a group including available
commands, group info, admin list.
+ /admins: Works only in group chats and displays a list of admins
without mentioning them.

* adds /json command

* fixes hidden sender display

* adds /user command
  • Loading branch information
jfowl authored Jul 2, 2019
1 parent 3c4dd11 commit 1fedd66
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 11 deletions.
120 changes: 110 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
const Telegraf = require('telegraf')
const bot = new Telegraf(process.env.BOT_TOKEN)

const TelegrafI18n = require('telegraf-i18n')
const rateLimit = require('telegraf-ratelimit')
const path = require('path')
const html = require('html-escaper')


const bot = new Telegraf(process.env.BOT_TOKEN)

const i18n = new TelegrafI18n({
defaultLanguage: 'en',
Expand All @@ -12,6 +15,18 @@ const i18n = new TelegrafI18n({

bot.use(i18n.middleware())

const limitConfig = {
window: 12000,
limit: 4,
keyGenerator: (ctx) => ctx.chat.id,
onLimitExceeded: (ctx, next) => {
// Only rate limit in group chats:
if (ctx.chat.type === 'private') next()
}
}

bot.use(rateLimit(limitConfig))

const getAge = require('./idage.js')

let botName = ''
Expand All @@ -27,7 +42,29 @@ const getDateString = (ctx) => {
return date.toUTCString()
}

const getAdminsString = async (chatId) => {
const admins = await bot.telegram.getChatAdministrators(chatId)
console.log(admins)
const resStr = admins
.map( a =>
(a.status === 'creator' ? '⭐️' : (a.user.is_bot ? '🤖' : '👤')) + ' '
+ (a.user.username?'<a href="https://t.me/'+a.user.username+'">':'')
+ html.escape(a.user.first_name+(a.user.last_name?' '+a.user.last_name:''))
.substring(0,25)
+ (a.user.username?'</a>':'')
)
.join('\n')

console.log('AdminsString:',"'"+resStr+"'")
return resStr
}

bot.start(ctx => {
// Filter out group chats
if (ctx.message.chat.type !== 'private') {
return false;
}

if (ctx.message.text === '/start idhelp') {
ctx.reply(ctx.i18n.t('idhelp'), { parse_mode: 'HTML' })
} else {
Expand All @@ -43,6 +80,51 @@ bot.help(ctx => {

const treeify = require('./treeify.js')

bot.command('admins', async ctx => {
if (ctx.chat.type === 'private' || ctx.chat.type === 'channel') {
ctx.replyWithHTML(ctx.i18n.t('admin_private_chat'))
} else {
const adminsStr = await getAdminsString(ctx.chat.id)
await ctx.replyWithHTML(ctx.i18n.t('admin_list', {adminsStr}),{
disable_web_page_preview: true
})
}
})

bot.command('json', ctx => {
if (ctx.message.reply_to_message === undefined) {
ctx.replyWithHTML(ctx.i18n.t('json_needs_reply'))
} else {
const rtm = ctx.message.reply_to_message
const jsonStr = html.escape(JSON.stringify(rtm, null, 2))
if (jsonStr.length > 4096) {
ctx.replyWithHTML(ctx.i18n.t('json_too_long'), {
disable_web_page_preview: true
})
} else {
ctx.replyWithHTML('<code>'+jsonStr+'</code>', {
disable_web_page_preview: true
})
}
}
})

bot.command('user', ctx => {
if (ctx.message.reply_to_message === undefined) {
ctx.replyWithHTML(ctx.i18n.t('user_needs_reply'))
} else {
const rtm = ctx.message.reply_to_message

rtm.from['created'] = getAgeString({message:rtm, i18n: ctx.i18n}, 'from')
ctx.replyWithHTML(treeify.renderTree(rtm.from, ctx.i18n.t('user_header')), {
disable_web_page_preview: true
})
}
})




bot.on('message', ctx => {
if (ctx.message.chat.type === 'private') {
handlePrivateChat(ctx)
Expand All @@ -51,7 +133,25 @@ bot.on('message', ctx => {
}
})

function handleGroupChat (ctx) {
async function handleGroupChat (ctx) {
if (ctx.message.new_chat_members !== undefined) {
if (ctx.message.new_chat_members.some(m => m.username === botName)) {
// Bot was just added to group

const adminsStr = await getAdminsString(ctx.chat.id)
console.log(adminsStr)

ctx.replyWithHTML(ctx.i18n.t('group_chat_start', {
chatInfoStr: treeify.renderTree(ctx.chat, ctx.i18n.t('this_chat_header')),
adminsStr
}),{
disable_web_page_preview: true
})
} else {
// Other new members
// TODO: For now, do nothing
}
}

}

Expand All @@ -78,16 +178,16 @@ function handlePrivateChat (ctx) {
) {
let fwfrom = ctx.message.forward_from

// Handle hidden account forwards:
if (fwfrom.id === -1001228946795) {
fwfrom = {
hidden: ctx.i18n.t('user_hid_account')
}
}

if (fwfrom.first_name !== undefined) {
fwfrom['created'] = getAgeString(ctx, 'forward_from')
}

}

if (ctx.message.forward_sender_name !== undefined) {
fwfrom = {
hidden: ctx.i18n.t('user_hid_account')
}
renders.push(
treeify.renderTree(fwfrom, ctx.i18n.t('forwarded_from_header'))
)
Expand Down
23 changes: 23 additions & 0 deletions locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,26 @@ poll_header: "📊 Poll"
inline_query_your_info_header: "👤 You"
inline_query_your_info_text: "Click here to send your info"
user_hid_account: "This user hid his account information in Telegram's privacy settings, so I can't tell you anything about him."
group_chat_start: |
GetIDsBot - Get Meta information in Telegram
<b>Available commands:</b>
» <code>/admins</code> to get a list of all current admins.
» <code>/json</code> in reply to a message to get it's bot api json variant.
» <code>/user</code> in reply to a message for info about that user.
${chatInfoStr}
<b>It's admins:</b>
${adminsStr}
ℹ️ <i>Commands are rate limited in group chats. I do not talk unless asked to do so.</i>
admin_list: |
<b>Admins:</b>
${adminsStr}
this_chat_header: "This chat"
user_header: "👤 User"
admin_private_chat: "A 1-on-1 chat does not have any admins. This command only works in group chats."
json_needs_reply: "Please reply to a specific message with this command."
json_too_long: "The resulting json of the referenced message is too long to send it here. As referenced <a href=\"https://core.telegram.org/bots/api#message\">here</a>, a message may only contain up to 4096 characters."
5 changes: 5 additions & 0 deletions locales/fa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ poll_header: "📊 استطلاع للرأي"
inline_query_your_info_header: "👤 شما"
inline_query_your_info_text: "اینجا رو بزن تا اطلاعاتت رو بفرستم"
user_hid_account: "این کاربر اطلاعات اکانتش رو توی تنظیمات حریم خصوصی تلگرام پنهان کرده، بنابراین در توانم نیست که چیزی درباره او بهت بگم."
group_chat_start: "NO TRANSLATION AVAILABLE YET."
admin_list: "NO TRANSLATION AVAILABLE YET."
admin_private_chat: "NO TRANSLATION AVAILABLE YET."
json_needs_reply: "NO TRANSLATION AVAILABLE YET."
json_too_long: "NO TRANSLATION AVAILABLE YET."
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"html-escaper": "^1.0.1",
"iso-lang-codes": "^2.0.0",
"telegraf": "^3.30.1",
"telegraf-i18n": "^6.5.0"
"telegraf-i18n": "^6.5.0",
"telegraf-ratelimit": "^2.0.0"
},
"devDependencies": {
"standard": "^12.0.1"
Expand Down

0 comments on commit 1fedd66

Please sign in to comment.