good stuff
README.md: Greatly updated. Will do more in future. administration.lua: Administrators will now be promoted to group admins upon joining an administrated group. drua-tg: Squashed all luacheck warnings. chatter.lua: Mostly rewritten; things actually make sense now. xkcd.lua: Slight styling change. patterns.lua: Squashed a warning.
This commit is contained in:
parent
f614e83497
commit
e08e5f64ee
258
README.md
258
README.md
@ -3,25 +3,24 @@ The plugin-wielding, multipurpose Telegram bot.
|
|||||||
|
|
||||||
[Public Bot](http://telegram.me/mokubot) | [Official Channel](http://telegram.me/otouto) | [Development Group](http://telegram.me/BotDevelopment)
|
[Public Bot](http://telegram.me/mokubot) | [Official Channel](http://telegram.me/otouto) | [Development Group](http://telegram.me/BotDevelopment)
|
||||||
|
|
||||||
otouto is an independently-developed Telegram API bot written in Lua. Originally conceived as a CLI script in February of 2015, otouto has since been open-sourced and migrated to the API, and is being developed to this day.
|
otouto is a plugin-based, IRC-style bot written for the [Telegram Bot API](http://core.telegram.org/bots/api). Originally written in February of 2015 as a set of Lua scripts to run on [telegram-cli](http://github.com/vysheng/tg), otouto was open-sourced and migrated to the bot API later in June that year.
|
||||||
|
|
||||||
otouto is free software; you are free to redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3. See LICENSE for details.
|
otouto is free software; you are free to redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3. See **LICENSE** for details.
|
||||||
|
|
||||||
| The Manual |
|
**The Manual**
|
||||||
|:-----------------------------------------|
|
|
||||||
| [Setup](#setup) |
|
| For Users | For Coders |
|
||||||
| [Bindings](#bindings) |
|
|:----------------------------------------------|:------------------------------|
|
||||||
| [Plugins](#plugins) |
|
| [Setup](#setup) | [Introduction](#introduction) |
|
||||||
| [Control plugins](#control-plugins) |
|
| [Control plugins](#control-plugins) | [Plugins](#plugins) |
|
||||||
| [administration.lua](#administrationlua) |
|
| [Group Administration](#group-administration) | [Bindings](#bindings) |
|
||||||
| [List of plugins](#list-of-plugins) |
|
| [List of plugins](#list-of-plugins) | [Output style](#output-style) |
|
||||||
| [Style](#style) |
|
| | [Contributors](#contributors) |
|
||||||
| [Contributors](#contributors) |
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
You _must_ have Lua (5.2+), luasocket, luasec, multipart-post, and dkjson installed. You should also have lpeg, though it is not required. It is recommended you install these with LuaRocks.
|
You _must_ have Lua (5.2+), luasocket, luasec, multipart-post, and dkjson installed. You should also have lpeg, though it is not required. It is recommended you install these with LuaRocks.
|
||||||
|
|
||||||
Clone the repository and set the following values in `config.lua`:
|
To get started, clone the repository and set the following values in `config.lua`:
|
||||||
|
|
||||||
- `bot_api_key` as your bot authorization token from the BotFather.
|
- `bot_api_key` as your bot authorization token from the BotFather.
|
||||||
- `admin` as your Telegram ID.
|
- `admin` as your Telegram ID.
|
||||||
@ -48,88 +47,6 @@ Note that certain plugins, such as translate.lua and greetings.lua, will require
|
|||||||
|
|
||||||
* * *
|
* * *
|
||||||
|
|
||||||
## Bindings
|
|
||||||
Calls to the Telegram bot API are performed with the `bindings.lua` file through the multipart-post library. otouto's bindings file supports all standard API methods and all arguments. Its main function, `bindings.request`, accepts four arguments: `self`, `method`, `parameters`, `file`. (At the very least, `self` should be a table containing `BASE_URL`, which is bot's API endpoint, ending with a slash, eg `https://api.telegram.org/bot123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ987654321/`.)
|
|
||||||
|
|
||||||
`method` is the name of the API method. `parameters` (optional) is a table of key/value pairs of the method's parameters to be sent with the method. `file` (super-optional) is a table of a single key/value pair, where the key is the name of the parameter and the value is the filename (if these are included in `parameters` instead, otouto will attempt to send the filename as a file ID).
|
|
||||||
|
|
||||||
Additionally, any method can be called as a key in the `bindings` table (for example, `bindings.getMe`). The `bindings.gen` function (which is also the __index function in its metatable) will forward its arguments to `bindings.request` in their proper form. In this way, the following two function calls are equivalent:
|
|
||||||
|
|
||||||
```
|
|
||||||
bindings.request(
|
|
||||||
self,
|
|
||||||
'sendMessage',
|
|
||||||
{
|
|
||||||
chat_id = 987654321,
|
|
||||||
text = 'Quick brown fox.',
|
|
||||||
reply_to_message_id = 54321,
|
|
||||||
disable_web_page_preview = false,
|
|
||||||
parse_method = 'Markdown'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
bindings.sendMessage(
|
|
||||||
self,
|
|
||||||
{
|
|
||||||
chat_id = 987654321,
|
|
||||||
text = 'Quick brown fox.',
|
|
||||||
reply_to_message_id = 54321,
|
|
||||||
disable_web_page_preview = false,
|
|
||||||
parse_method = 'Markdown'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Furthermore, `utilities.lua` provides two "shortcut" functions to mimic the behavior of otouto's old bindings: `send_message` and `send_reply`. `send_message` accepts these arguments: `self`, `chat_id`, `text`, `disable_web_page_preview`, `reply_to_message_id`, `use_markdown`. The following function call is equivalent to the two above:
|
|
||||||
|
|
||||||
```
|
|
||||||
utilities.send_message(self, 987654321, 'Quick brown fox.', false, 54321, true)
|
|
||||||
```
|
|
||||||
|
|
||||||
Uploading a file for the `sendPhoto` method would look like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
bindings.sendPhoto(self, { chat_id = 987654321 }, { photo = 'rarepepe.jpg' } )
|
|
||||||
```
|
|
||||||
|
|
||||||
and using `sendPhoto` with a file ID would look like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
bindings.sendPhoto(self, { chat_id = 987654321, photo = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789' } )
|
|
||||||
```
|
|
||||||
|
|
||||||
Upon success, bindings will return the deserialized result from the API. Upon failure, it will return false and the result. In the case of a connection error, it will return two false values. If an invalid method name is given, bindings will throw an exception. This is to mimic the behavior of more conventional bindings as well as to prevent "silent errors".
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## Plugins
|
|
||||||
otouto uses a robust plugin system, similar to that of yagop's [Telegram-Bot](http://github.com/yagop/telegram-bot). The aim of the otouto project is to contain any desirable bot feature within one universal bot framework.
|
|
||||||
|
|
||||||
Most plugins are intended for public use, but a few are for other purposes, like those used alongside [Liberbot](#liberbot-related-plugins), or for [use by the bot's owner](#control-plugins). See [here](#list-of-plugins) for a list of plugins.
|
|
||||||
|
|
||||||
A plugin can have five components, and two of them are required:
|
|
||||||
|
|
||||||
| Component | Description | Required? |
|
|
||||||
|:----------------|:---------------------------------------------|:----------|
|
|
||||||
| plugin:action | Main function. Expects `msg` table as an argument. | Y |
|
|
||||||
| plugin.triggers | Table of triggers for the plugin. Uses Lua patterns. | Y |
|
|
||||||
| plugin:init | Optional function run when the plugin is loaded. | N |
|
|
||||||
| plugin:cron | Optional function to be called every minute. | N |
|
|
||||||
| plugin.command | Basic command and syntax. Listed in the help text. | N |
|
|
||||||
| plugin.doc | Usage for the plugin. Returned by "/help $command". | N |
|
|
||||||
|
|
||||||
The `bot:on_msg_receive` function adds a few variables to the `msg` table for your convenience. These are self-explanatory: `msg.from.id_str`, `msg.to.id_str`, `msg.chat.id_str`, `msg.text_lower`, `msg.from.name`.
|
|
||||||
|
|
||||||
Return values from `plugin:action` are optional, but they do effect the flow. If it returns a table, that table will become `msg`, and `on_msg_receive` will continue with that. If it returns `true`, it will continue with the current `msg`.
|
|
||||||
|
|
||||||
When an action or cron function fails, the exception is caught and passed to the `handle_exception` utilty and is either printed to the console or send to the chat/channel defined in `log_chat` in config.lua.
|
|
||||||
|
|
||||||
Interactions with the bot API are straightforward. See the [Bindings section](#bindings) for details.
|
|
||||||
|
|
||||||
Several functions used in multiple plugins are defined in utilities.lua. Refer to that file for usage and documentation.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
## Control plugins
|
## Control plugins
|
||||||
Some plugins are designed to be used by the bot's owner. Here are some examples, how they're used, and what they do.
|
Some plugins are designed to be used by the bot's owner. Here are some examples, how they're used, and what they do.
|
||||||
|
|
||||||
@ -144,10 +61,8 @@ Some plugins are designed to be used by the bot's owner. Here are some examples,
|
|||||||
|
|
||||||
* * *
|
* * *
|
||||||
|
|
||||||
## administration.lua
|
## Group Administration
|
||||||
The administration plugin enables self-hosted, single-realm group administration, supporting both normal groups and supergroups. This works by sending TCP commands to an instance of tg running on the owner's account.
|
The administration plugin enables self-hosted, single-realm group administration, supporting both normal groups and supergroups whch are owned by the bot owner. This works by sending TCP commands to an instance of tg running on the owner's account.
|
||||||
|
|
||||||
**For best results, make your bot an administrator of any group it administrates.**
|
|
||||||
|
|
||||||
To get started, run `./tg-install.sh`. Note that this script is written for Ubuntu/Debian. If you're running Arch (the only acceptable alternative), you'll have to do it yourself. If that is the case, note that otouto uses the "test" branch of tg, and the AUR package `telegram-cli-git` will not be sufficient, as it does not have support for supergroups yet.
|
To get started, run `./tg-install.sh`. Note that this script is written for Ubuntu/Debian. If you're running Arch (the only acceptable alternative), you'll have to do it yourself. If that is the case, note that otouto uses the "test" branch of tg, and the AUR package `telegram-cli-git` will not be sufficient, as it does not have support for supergroups yet.
|
||||||
|
|
||||||
@ -279,35 +194,138 @@ Additionally, antiflood can be configured to automatically ban a user after he h
|
|||||||
|
|
||||||
* * *
|
* * *
|
||||||
|
|
||||||
## Style
|
## Introduction
|
||||||
Bot output from every plugin should follow a consistent style. This style is easily observed interacting with the bot.
|
#todo
|
||||||
Titles should be either **bold** (along with their colons) or a [link](http://otou.to) (with plaintext colons) to the content's source. Names should be _italic_. Numbered lists should use bold numbers followed by a bold period followed by a space. Unnumbered lists should use the • bullet point followed by a space. Descriptions and information should be in plaintext, although "flavor" text should be italic. Technical information should be `monospace`. Links should be named.
|
|
||||||
The standard count for plugins which return multiple results is eight results in a private message, and four results elsewhere. This is a trivial number, but consistency is noticeable and desirable.
|
## Plugins
|
||||||
|
otouto uses a robust plugin system, similar to yagop's [Telegram-Bot](http://github.com/yagop/telegram-bot). The aim of the otouto project is to contain any desirable bot feature within one universal bot framework.
|
||||||
|
|
||||||
|
Most plugins are intended for public use, but a few are for other purposes, like those used alongside [Liberbot](#liberbot-related-plugins), or for [use by the bot's owner](#control-plugins). See [here](#list-of-plugins) for a list of plugins.
|
||||||
|
|
||||||
|
A plugin can have five components, and two of them are required:
|
||||||
|
|
||||||
|
| Component | Description | Required? |
|
||||||
|
|:----------------|:---------------------------------------------|:----------|
|
||||||
|
| plugin:action | Main function. Expects `msg` table as an argument. | Y |
|
||||||
|
| plugin.triggers | Table of triggers for the plugin. Uses Lua patterns. | Y |
|
||||||
|
| plugin:init | Optional function run when the plugin is loaded. | N |
|
||||||
|
| plugin:cron | Optional function to be called every minute. | N |
|
||||||
|
| plugin.command | Basic command and syntax. Listed in the help text. | N |
|
||||||
|
| plugin.doc | Usage for the plugin. Returned by "/help $command". | N |
|
||||||
|
|
||||||
|
The `bot:on_msg_receive` function adds a few variables to the `msg` table for your convenience. These are self-explanatory: `msg.from.id_str`, `msg.to.id_str`, `msg.chat.id_str`, `msg.text_lower`, `msg.from.name`.
|
||||||
|
|
||||||
|
Return values from `plugin:action` are optional, but they do effect the flow. If it returns a table, that table will become `msg`, and `on_msg_receive` will continue with that. If it returns `true`, it will continue with the current `msg`.
|
||||||
|
|
||||||
|
When an action or cron function fails, the exception is caught and passed to the `handle_exception` utilty and is either printed to the console or send to the chat/channel defined in `log_chat` in config.lua.
|
||||||
|
|
||||||
|
Interactions with the bot API are straightforward. See the [Bindings section](#bindings) for details.
|
||||||
|
|
||||||
|
Several functions used in multiple plugins are defined in utilities.lua. Refer to that file for usage and documentation.
|
||||||
|
|
||||||
|
* * *
|
||||||
|
|
||||||
|
## Bindings
|
||||||
|
Calls to the Telegram bot API are performed with the `bindings.lua` file through the multipart-post library. otouto's bindings file supports all standard API methods and all arguments. Its main function, `bindings.request`, accepts four arguments: `self`, `method`, `parameters`, `file`. (At the very least, `self` should be a table containing `BASE_URL`, which is bot's API endpoint, ending with a slash, eg `https://api.telegram.org/bot123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ987654321/`.)
|
||||||
|
|
||||||
|
`method` is the name of the API method. `parameters` (optional) is a table of key/value pairs of the method's parameters to be sent with the method. `file` (super-optional) is a table of a single key/value pair, where the key is the name of the parameter and the value is the filename (if these are included in `parameters` instead, otouto will attempt to send the filename as a file ID).
|
||||||
|
|
||||||
|
Additionally, any method can be called as a key in the `bindings` table (for example, `bindings.getMe`). The `bindings.gen` function (which is also the __index function in its metatable) will forward its arguments to `bindings.request` in their proper form. In this way, the following two function calls are equivalent:
|
||||||
|
|
||||||
|
```
|
||||||
|
bindings.request(
|
||||||
|
self,
|
||||||
|
'sendMessage',
|
||||||
|
{
|
||||||
|
chat_id = 987654321,
|
||||||
|
text = 'Quick brown fox.',
|
||||||
|
reply_to_message_id = 54321,
|
||||||
|
disable_web_page_preview = false,
|
||||||
|
parse_method = 'Markdown'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
bindings.sendMessage(
|
||||||
|
self,
|
||||||
|
{
|
||||||
|
chat_id = 987654321,
|
||||||
|
text = 'Quick brown fox.',
|
||||||
|
reply_to_message_id = 54321,
|
||||||
|
disable_web_page_preview = false,
|
||||||
|
parse_method = 'Markdown'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Furthermore, `utilities.lua` provides two "shortcut" functions to mimic the behavior of otouto's old bindings: `send_message` and `send_reply`. `send_message` accepts these arguments: `self`, `chat_id`, `text`, `disable_web_page_preview`, `reply_to_message_id`, `use_markdown`. The following function call is equivalent to the two above:
|
||||||
|
|
||||||
|
```
|
||||||
|
utilities.send_message(self, 987654321, 'Quick brown fox.', false, 54321, true)
|
||||||
|
```
|
||||||
|
|
||||||
|
Uploading a file for the `sendPhoto` method would look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
bindings.sendPhoto(self, { chat_id = 987654321 }, { photo = 'rarepepe.jpg' } )
|
||||||
|
```
|
||||||
|
|
||||||
|
and using `sendPhoto` with a file ID would look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
bindings.sendPhoto(self, { chat_id = 987654321, photo = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789' } )
|
||||||
|
```
|
||||||
|
|
||||||
|
Upon success, bindings will return the deserialized result from the API. Upon failure, it will return false and the result. In the case of a connection error, it will return two false values. If an invalid method name is given, bindings will throw an exception. This is to mimic the behavior of more conventional bindings as well as to prevent "silent errors".
|
||||||
|
|
||||||
|
* * *
|
||||||
|
|
||||||
|
## Output style
|
||||||
|
otouto plugins should maintain a consistent visual style in their output. This provides a recognizable and comfortable user experience.
|
||||||
|
|
||||||
|
### Titles
|
||||||
|
Title lines should be **bold**, including any names and trailing punctuation (such as colons). The exception to this rule is if the title line includes a query, which should be _italic_. It is also acceptable to have a link somewhere inside a title, usually within parentheses. eg:
|
||||||
|
|
||||||
|
> **Star Wars: Episode IV - A New Hope (1977)**
|
||||||
|
>
|
||||||
|
> **Search results for** _star wars_**:**
|
||||||
|
>
|
||||||
|
> **Changelog for otouto (**[Github](http://github.com/topkecleon/otouto)**):**
|
||||||
|
|
||||||
|
### Lists
|
||||||
|
Numerated lists should be done with the number and its following punctuation bolded. Unnumbered lists should use the bullet character ( • ). eg:
|
||||||
|
|
||||||
|
> **1.** Life as a quick brown fox.
|
||||||
|
>
|
||||||
|
> **2.** The art of jumping over lazy dogs.
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
> • Life as a quick brown fox.
|
||||||
|
>
|
||||||
|
> • The art of jumping over lazy dogs.
|
||||||
|
|
||||||
|
### Links
|
||||||
|
Always name your links. Even then, use them with discretion. Excessive links make a post look messy. Links are reasonable when a user may want to learn more about something, but should be avoided when all desirable information is provided. One appropriate use of linking is to provide a preview of an image, as xkcd.lua and apod.lua do.
|
||||||
|
|
||||||
|
### Other Stuff
|
||||||
|
User IDs should appear within brackets, monospaced (`[123456789]`). Descriptions and information should be in plain text, but "flavor" text should be italic. The standard size for arbitrary lists (such as search results) is eight within a private conversation and four elsewhere. This is a trivial pair of numbers (leftover from the deprecated Google search API), but consistency is noticeable and desirable.
|
||||||
|
|
||||||
|
* * *
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
Everybody is free to contribute to otouto. If you are interested, you are invited to fork the [repo](http://github.com/topkecleon/otouto) and start making pull requests. If you have an idea and you are not sure how to implement it, open an issue or bring it up in the Bot Development group.
|
Everybody is free to contribute to otouto. If you are interested, you are invited to [fork the repo](http://github.com/topkecleon/otouto/fork) and start making pull requests. If you have an idea and you are not sure how to implement it, open an issue or bring it up in the [Bot Development group](http://telegram.me/BotDevelopment).
|
||||||
|
|
||||||
The creator and maintainer of otouto is [topkecleon](http://github.com/topkecleon). He can be contacted via [Telegram](http://telegram.me/topkecleon), [Twitter](http://twitter.com/topkecleon), or [email](mailto:drew@otou.to).
|
The creator and maintainer of otouto is [topkecleon](http://github.com/topkecleon). He can be contacted via [Telegram](http://telegram.me/topkecleon), [Twitter](http://twitter.com/topkecleon), or [email](mailto:drew@otou.to).
|
||||||
|
|
||||||
|
[List of contributors.](https://github.com/topkecleon/otouto/graphs/contributors)
|
||||||
|
|
||||||
There are a a few ways to contribute if you are not a programmer. For one, your feedback is always appreciated. Drop me a line on Telegram or on Twitter. Secondly, we are always looking for new ideas for plugins. Most new plugins start with community input. Feel free to suggest them on Github or in the Bot Dev group. You can also donate Bitcoin to the following address:
|
There are a a few ways to contribute if you are not a programmer. For one, your feedback is always appreciated. Drop me a line on Telegram or on Twitter. Secondly, we are always looking for new ideas for plugins. Most new plugins start with community input. Feel free to suggest them on Github or in the Bot Dev group. You can also donate Bitcoin to the following address:
|
||||||
`1BxegZJ73hPu218UrtiY8druC7LwLr82gS`
|
`1BxegZJ73hPu218UrtiY8druC7LwLr82gS`
|
||||||
|
|
||||||
Contributions are appreciated in any form. Monetary contributions will go toward server costs. Both programmers and donators will be eternally honored (at their discretion) on this page.
|
Contributions are appreciated in all forms. Monetary contributions will go toward server costs. Donators will be eternally honored (at their discretion) on this page.
|
||||||
|
|
||||||
| Contributors |
|
| Donators (in chronological order) |
|
||||||
|:----------------------------------------------|
|
|:----------------------------------------------|
|
||||||
| [bb010g](http://github.com/bb010g) |
|
| [n8 c00](http://telegram.me/n8_c00) |
|
||||||
| [Juan Potato](http://github.com/JuanPotato) |
|
|
||||||
| [Tiago Danin](http://github.com/TiagoDanin) |
|
|
||||||
| [Ender](http://github.com/luksireiku) |
|
|
||||||
| [Iman Daneshi](http://github.com/Imandaneshi) |
|
|
||||||
| [HeitorPB](http://github.com/heitorPB) |
|
|
||||||
| [Akronix](http://github.com/Akronix) |
|
|
||||||
| [Ville](http://github.com/cwxda) |
|
|
||||||
| [dogtopus](http://github.com/dogtopus) |
|
|
||||||
|
|
||||||
| Donators |
|
|
||||||
|:----------------------------------------------|
|
|
||||||
| [n8](http://telegram.me/n8_c00) |
|
|
||||||
| [Alex](http://telegram.me/sandu) |
|
| [Alex](http://telegram.me/sandu) |
|
||||||
| [Brayden Banks](http://telegram.me/bb010g) |
|
| [Brayden](http://telegram.me/bb010g) |
|
||||||
|
44
drua-tg.lua
44
drua-tg.lua
@ -63,23 +63,24 @@ drua.send = function(command, do_receive)
|
|||||||
end
|
end
|
||||||
|
|
||||||
drua.message = function(target, text)
|
drua.message = function(target, text)
|
||||||
local target = format_target(target)
|
target = format_target(target)
|
||||||
local text = escape(text)
|
text = escape(text)
|
||||||
local command = 'msg %s "%s"'
|
local command = 'msg %s "%s"'
|
||||||
command = command:format(target, text)
|
command = command:format(target, text)
|
||||||
return drua.send(command)
|
return drua.send(command)
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.send_photo = function(target, photo)
|
drua.send_photo = function(target, photo)
|
||||||
local target = format_target(target)
|
target = format_target(target)
|
||||||
local command = 'send_photo %s %s'
|
local command = 'send_photo %s %s'
|
||||||
command = command:format(target, photo)
|
command = command:format(target, photo)
|
||||||
return drua.send(command)
|
return drua.send(command)
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.add_user = function(chat, target)
|
drua.add_user = function(chat, target)
|
||||||
local chat,a = format_target(chat)
|
local a
|
||||||
local target = format_target(target)
|
chat, a = format_target(chat)
|
||||||
|
target = format_target(target)
|
||||||
local command = comtab.add[a]:format(chat, target)
|
local command = comtab.add[a]:format(chat, target)
|
||||||
return drua.send(command)
|
return drua.send(command)
|
||||||
end
|
end
|
||||||
@ -87,55 +88,64 @@ end
|
|||||||
drua.kick_user = function(chat, target)
|
drua.kick_user = function(chat, target)
|
||||||
-- Get the group info so tg will recognize the target.
|
-- Get the group info so tg will recognize the target.
|
||||||
drua.get_info(chat)
|
drua.get_info(chat)
|
||||||
local chat,a = format_target(chat)
|
local a
|
||||||
local target = format_target(target)
|
chat, a = format_target(chat)
|
||||||
|
target = format_target(target)
|
||||||
local command = comtab.kick[a]:format(chat, target)
|
local command = comtab.kick[a]:format(chat, target)
|
||||||
return drua.send(command)
|
return drua.send(command)
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.rename_chat = function(chat, name)
|
drua.rename_chat = function(chat, name)
|
||||||
local chat,a = format_target(chat)
|
local a
|
||||||
|
chat, a = format_target(chat)
|
||||||
local command = comtab.rename[a]:format(chat, name)
|
local command = comtab.rename[a]:format(chat, name)
|
||||||
return drua.send(command)
|
return drua.send(command)
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.export_link = function(chat)
|
drua.export_link = function(chat)
|
||||||
local chat,a = format_target(chat)
|
local a
|
||||||
|
chat, a = format_target(chat)
|
||||||
local command = comtab.link[a]:format(chat)
|
local command = comtab.link[a]:format(chat)
|
||||||
return drua.send(command, true)
|
return drua.send(command, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.get_photo = function(chat)
|
drua.get_photo = function(chat)
|
||||||
local chat,a = format_target(chat)
|
local a
|
||||||
|
chat, a = format_target(chat)
|
||||||
local command = comtab.photo_get[a]:format(chat)
|
local command = comtab.photo_get[a]:format(chat)
|
||||||
local output = drua.send(command, true)
|
local output = drua.send(command, true)
|
||||||
if output:match('FAIL') then return false end
|
if output:match('FAIL') then
|
||||||
|
return false
|
||||||
|
else
|
||||||
return output:match('Saved to (.+)')
|
return output:match('Saved to (.+)')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.set_photo = function(chat, photo)
|
drua.set_photo = function(chat, photo)
|
||||||
local chat,a = format_target(chat)
|
local a
|
||||||
|
chat, a = format_target(chat)
|
||||||
local command = comtab.photo_set[a]:format(chat, photo)
|
local command = comtab.photo_set[a]:format(chat, photo)
|
||||||
return drua.send(command)
|
return drua.send(command)
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.get_info = function(target)
|
drua.get_info = function(target)
|
||||||
local target,a = format_target(target)
|
local a
|
||||||
|
target, a = format_target(target)
|
||||||
local command = comtab.info[a]:format(target)
|
local command = comtab.info[a]:format(target)
|
||||||
return drua.send(command, true)
|
return drua.send(command, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.channel_set_admin = function(chat, user, rank)
|
drua.channel_set_admin = function(chat, user, rank)
|
||||||
local chat = format_target(chat)
|
chat = format_target(chat)
|
||||||
local user = format_target(user)
|
user = format_target(user)
|
||||||
local command = 'channel_set_admin %s %s %s'
|
local command = 'channel_set_admin %s %s %s'
|
||||||
command = command:format(chat, user, rank)
|
command = command:format(chat, user, rank)
|
||||||
return drua.send(command)
|
return drua.send(command)
|
||||||
end
|
end
|
||||||
|
|
||||||
drua.channel_set_about = function(chat, text)
|
drua.channel_set_about = function(chat, text)
|
||||||
local chat = format_target(chat)
|
chat = format_target(chat)
|
||||||
local text = escape(text)
|
text = escape(text)
|
||||||
local command = 'channel_set_about %s "%s"'
|
local command = 'channel_set_about %s "%s"'
|
||||||
command = command:format(chat, text)
|
command = command:format(chat, text)
|
||||||
return drua.send(command)
|
return drua.send(command)
|
||||||
|
@ -272,8 +272,7 @@ function administration.init_command(self_)
|
|||||||
|
|
||||||
action = function(self, msg, group)
|
action = function(self, msg, group)
|
||||||
|
|
||||||
local rank = administration.get_rank(self, msg.from.id, msg.chat.id)
|
local rank = administration.get_rank(self, msg.from.id, msg.chat.id)
|
||||||
|
|
||||||
local user = {}
|
local user = {}
|
||||||
|
|
||||||
if rank < 2 then
|
if rank < 2 then
|
||||||
@ -343,27 +342,27 @@ local rank = administration.get_rank(self, msg.from.id, msg.chat.id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local new_user = user
|
local new_user = user
|
||||||
|
local new_rank = rank
|
||||||
|
|
||||||
if msg.new_chat_participant then
|
if msg.new_chat_participant then
|
||||||
|
|
||||||
|
-- I hate typing this out.
|
||||||
|
local noob = msg.new_chat_participant
|
||||||
|
|
||||||
-- We'll make a new table for the new guy, unless he's also
|
-- We'll make a new table for the new guy, unless he's also
|
||||||
-- the original guy.
|
-- the original guy.
|
||||||
if msg.new_chat_participant.id ~= msg.from.id then
|
if msg.new_chat_participant.id ~= msg.from.id then
|
||||||
new_user = {}
|
new_user = {}
|
||||||
|
new_rank = administration.get_rank(self,noob.id, msg.chat.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- I hate typing this out.
|
if new_rank == 0 then
|
||||||
local noob = msg.new_chat_participant
|
|
||||||
|
|
||||||
if administration.get_rank(self, msg.new_chat_participant.id, msg.chat.id) < 2 then
|
|
||||||
|
|
||||||
-- banned
|
|
||||||
if administration.get_rank(self, noob.id, msg.chat.id) == 0 then
|
|
||||||
new_user.do_kick = true
|
new_user.do_kick = true
|
||||||
new_user.dont_unban = true
|
new_user.dont_unban = true
|
||||||
new_user.reason = 'banned'
|
new_user.reason = 'banned'
|
||||||
new_user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.'
|
new_user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.'
|
||||||
elseif group.flags[3] and ( -- antisquig++
|
elseif new_rank == 1 then
|
||||||
|
if group.flags[3] and ( -- antisquig++
|
||||||
noob.name:match(utilities.char.arabic)
|
noob.name:match(utilities.char.arabic)
|
||||||
or noob.name:match(utilities.char.rtl_override)
|
or noob.name:match(utilities.char.rtl_override)
|
||||||
or noob.name:match(utilities.char.rtl_mark)
|
or noob.name:match(utilities.char.rtl_mark)
|
||||||
@ -371,11 +370,20 @@ local rank = administration.get_rank(self, msg.from.id, msg.chat.id)
|
|||||||
new_user.do_kick = true
|
new_user.do_kick = true
|
||||||
new_user.reason = 'antisquig++'
|
new_user.reason = 'antisquig++'
|
||||||
new_user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title)
|
new_user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title)
|
||||||
elseif group.flags[4] and noob.username and noob.username:match('bot') and rank < 2 then
|
elseif ( -- antibot
|
||||||
|
group.flags[4]
|
||||||
|
and noob.username
|
||||||
|
and noob.username:match('bot')
|
||||||
|
and rank < 2
|
||||||
|
) then
|
||||||
new_user.do_kick = true
|
new_user.do_kick = true
|
||||||
new_user.reason = 'antibot'
|
new_user.reason = 'antibot'
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- Make the new user a group admin if he's a mod or higher.
|
||||||
|
if msg.chat.type == 'supergroup' then
|
||||||
|
drua.channel_set_admin(msg.chat.id, msg.new_chat_participant.id, 2)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif msg.new_chat_title then
|
elseif msg.new_chat_title then
|
||||||
@ -1165,13 +1173,20 @@ local rank = administration.get_rank(self, msg.from.id, msg.chat.id)
|
|||||||
local target = administration.get_target(self, msg)
|
local target = administration.get_target(self, msg)
|
||||||
if target.err then
|
if target.err then
|
||||||
utilities.send_reply(self, msg, target.err)
|
utilities.send_reply(self, msg, target.err)
|
||||||
elseif target.rank ~= 4 then
|
else
|
||||||
|
for chat_id, group in pairs(self.database.administration.groups) do
|
||||||
|
if group.grouptype == 'supergroup' then
|
||||||
|
drua.channel_set_admin(chat_id, target.id, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if target.rank ~= 4 then
|
||||||
utilities.send_reply(self, msg, target.name .. ' is not an administrator.')
|
utilities.send_reply(self, msg, target.name .. ' is not an administrator.')
|
||||||
else
|
else
|
||||||
self.database.administration.admins[target.id_str] = nil
|
self.database.administration.admins[target.id_str] = nil
|
||||||
utilities.send_reply(self, msg, target.name .. ' is no longer an administrator.')
|
utilities.send_reply(self, msg, target.name .. ' is no longer an administrator.')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{ -- /gadd
|
{ -- /gadd
|
||||||
|
@ -16,42 +16,38 @@ function chatter:init()
|
|||||||
end
|
end
|
||||||
|
|
||||||
chatter.triggers = {
|
chatter.triggers = {
|
||||||
'',
|
''
|
||||||
'^' .. self.info.first_name .. ',',
|
|
||||||
'^@' .. self.info.username .. ','
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
chatter.base_url = 'http://%sapi.simsimi.com/request.p?key=%s&lc=%s&ft=1.0&text=%s'
|
||||||
|
|
||||||
function chatter:action(msg)
|
function chatter:action(msg)
|
||||||
|
|
||||||
if msg.text == '' then return end
|
if msg.text == '' then return true end
|
||||||
|
|
||||||
-- This is awkward, but if you have a better way, please share.
|
if (
|
||||||
if msg.text_lower:match('^' .. self.info.first_name .. ',')
|
not (
|
||||||
or msg.text_lower:match('^@' .. self.info.username .. ',') then
|
msg.text_lower:match('^'..self.info.first_name:lower()..',')
|
||||||
elseif msg.text:match('^/') then
|
or msg.text_lower:match('^@'..self.info.username:lower()..',')
|
||||||
return true
|
or msg.from.id == msg.chat.id
|
||||||
-- Uncomment the following line for Al Gore-like reply chatter.
|
--Uncomment the following line for Al Gore-like conversation.
|
||||||
-- elseif msg.reply_to_message and msg.reply_to_message.from.id == bot.id then
|
--or (msg.reply_to_message and msg.reply_to_message.from.id == self.info.id)
|
||||||
elseif msg.from.id == msg.chat.id then
|
)
|
||||||
else
|
or msg.text:match('^/')
|
||||||
|
or msg.text == ''
|
||||||
|
) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
bindings.sendChatAction(self, { action = 'typing' } )
|
bindings.sendChatAction(self, { action = 'typing' } )
|
||||||
|
|
||||||
local input = msg.text_lower
|
local input = msg.text_lower:gsub(self.info.first_name, 'simsimi')
|
||||||
input = input:gsub(self.info.first_name, 'simsimi')
|
|
||||||
input = input:gsub('@'..self.info.username, 'simsimi')
|
input = input:gsub('@'..self.info.username, 'simsimi')
|
||||||
|
|
||||||
local sandbox
|
local sandbox = self.config.simsimi_trial and 'sandbox.' or ''
|
||||||
if self.config.simsimi_trial then
|
|
||||||
sandbox = 'sandbox.'
|
|
||||||
else
|
|
||||||
sandbox = '' -- NO Sandbox
|
|
||||||
end
|
|
||||||
|
|
||||||
local url = 'http://' ..sandbox.. 'api.simsimi.com/request.p?key=' ..self.config.simsimi_key.. '&lc=' ..self.config.lang.. '&ft=1.0&text=' .. URL.escape(input)
|
local url = chatter.base_url:format(sandbox, self.config.simsimi_key, self.config.lang, URL.escape(input))
|
||||||
|
|
||||||
local jstr, res = HTTP.request(url)
|
local jstr, res = HTTP.request(url)
|
||||||
if res ~= 200 then
|
if res ~= 200 then
|
||||||
@ -60,31 +56,22 @@ function chatter:action(msg)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local jdat = JSON.decode(jstr)
|
local jdat = JSON.decode(jstr)
|
||||||
if not jdat.response then
|
if not jdat.response or jdat.response:match('^I HAVE NO RESPONSE.') then
|
||||||
utilities.send_message(self, msg.chat.id, self.config.errors.chatter_response)
|
utilities.send_message(self, msg.chat.id, self.config.errors.chatter_response)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local output = jdat.response
|
local output = jdat.response
|
||||||
|
|
||||||
if output:match('^I HAVE NO RESPONSE.') then
|
-- Clean up the response here.
|
||||||
output = self.config.errors.chatter_response
|
output = utilities.trim(output)
|
||||||
end
|
-- Simsimi will often refer to itself. Replace "simsimi" with the bot name.
|
||||||
|
output = output:gsub('%aimi?%aimi?', self.info.first_name)
|
||||||
-- Let's clean up the response a little. Capitalization & punctuation.
|
-- Self-explanatory.
|
||||||
local filter = {
|
output = output:gsub('USER', msg.from.first_name)
|
||||||
['%aimi?%aimi?'] = self.info.first_name,
|
-- Capitalize the first letter.
|
||||||
['^%s*(.-)%s*$'] = '%1',
|
output = output:gsub('^%l', string.upper)
|
||||||
['^%l'] = string.upper,
|
-- Add a period if there is no punctuation.
|
||||||
['USER'] = msg.from.first_name
|
output = output:gsub('%P$', '%1.')
|
||||||
}
|
|
||||||
|
|
||||||
for k,v in pairs(filter) do
|
|
||||||
output = string.gsub(output, k, v)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not string.match(output, '%p$') then
|
|
||||||
output = output .. '.'
|
|
||||||
end
|
|
||||||
|
|
||||||
utilities.send_message(self, msg.chat.id, output)
|
utilities.send_message(self, msg.chat.id, output)
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ function patterns:action(msg)
|
|||||||
local output = msg.reply_to_message.text or ''
|
local output = msg.reply_to_message.text or ''
|
||||||
local m1, m2 = msg.text:match('^/?s/(.-)/(.-)/?$')
|
local m1, m2 = msg.text:match('^/?s/(.-)/(.-)/?$')
|
||||||
if not m2 then return true end
|
if not m2 then return true end
|
||||||
local res, output = pcall(
|
local res
|
||||||
|
res, output = pcall(
|
||||||
function()
|
function()
|
||||||
return output:gsub(m1, m2)
|
return output:gsub(m1, m2)
|
||||||
end
|
end
|
||||||
|
@ -49,7 +49,7 @@ function xkcd:action(msg)
|
|||||||
end
|
end
|
||||||
local jdat = JSON.decode(jstr)
|
local jdat = JSON.decode(jstr)
|
||||||
|
|
||||||
local output = '*' .. jdat.safe_title .. '* ([' .. jdat.num .. '](' .. jdat.img .. '))\n' .. jdat.alt
|
local output = '*' .. jdat.safe_title .. ' (*[' .. jdat.num .. '](' .. jdat.img .. ')*)*\n_' .. jdat.alt:gsub('_', '\\_') .. '_'
|
||||||
|
|
||||||
utilities.send_message(self, msg.chat.id, output, false, nil, true)
|
utilities.send_message(self, msg.chat.id, output, false, nil, true)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user