From 85fc920a0c4976adcd3110c633bbb6caacf00c84 Mon Sep 17 00:00:00 2001 From: LOMS Date: Mon, 25 Jan 2021 15:43:47 +0300 Subject: [PATCH] Some improvements to community.general.telegram (#1642) * Added some flexibility to cover latest and future possible changes in Telegram API. * Added changelog fragment for changes in community.general.telegram * Apply suggestions from code review Co-authored-by: Abhijeet Kasurde Co-authored-by: Andrew Klychkov Co-authored-by: Felix Fontein Co-authored-by: Nikolay Lomov Co-authored-by: Abhijeet Kasurde Co-authored-by: Andrew Klychkov Co-authored-by: Felix Fontein --- .github/BOTMETA.yml | 2 +- changelogs/fragments/telegram-api-update.yml | 5 + plugins/modules/notification/telegram.py | 119 ++++++++++++++----- 3 files changed, 92 insertions(+), 34 deletions(-) create mode 100644 changelogs/fragments/telegram-api-update.yml diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 572572c51f..f47a6223c7 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -529,7 +529,7 @@ files: $modules/notification/syslogger.py: maintainers: garbled1 $modules/notification/telegram.py: - maintainers: tyouxa + maintainers: tyouxa loms $modules/notification/twilio.py: maintainers: makaimc $modules/notification/typetalk.py: diff --git a/changelogs/fragments/telegram-api-update.yml b/changelogs/fragments/telegram-api-update.yml new file mode 100644 index 0000000000..c6422cf5f5 --- /dev/null +++ b/changelogs/fragments/telegram-api-update.yml @@ -0,0 +1,5 @@ +minor_changes: + - telegram - now can call any methods in Telegram bot API. + Previously this module was hardcoded to use "SendMessage" only. + Usage of "SendMessage" API method was also librated, + and now you can specify any arguments you need, for example, "disable_notificaton" (https://github.com/ansible-collections/community.general/pull/1642). diff --git a/plugins/modules/notification/telegram.py b/plugins/modules/notification/telegram.py index c1ef841c4a..68f55ae479 100644 --- a/plugins/modules/notification/telegram.py +++ b/plugins/modules/notification/telegram.py @@ -11,44 +11,80 @@ __metaclass__ = type DOCUMENTATION = ''' module: telegram -author: "Artem Feofanov (@tyouxa)" +author: + - "Artem Feofanov (@tyouxa)" + - "Nikolai Lomov (@lomserman)" short_description: module for sending notifications via telegram description: - - Send notifications via telegram bot, to a verified group or user + - Send notifications via telegram bot, to a verified group or user. + - Also, the user may try to use any other telegram bot API method, if you specify I(api_method) argument. notes: - You will require a telegram account and create telegram bot to use this module. + - The options I(msg), I(msg_format) and I(chat_id) have been deprecated and will be removed in community.general + 4.0.0. Use the corresponding variables in I(api_args) instead. See the examples for how that works. options: - msg: - type: str - description: - - What message you wish to send. - required: true - msg_format: - type: str - description: - - Message format. Formatting options `markdown` and `html` described in - Telegram API docs (https://core.telegram.org/bots/api#formatting-options). - If option `plain` set, message will not be formatted. - default: plain - choices: [ "plain", "markdown", "html" ] token: type: str description: - Token identifying your telegram bot. required: true + api_method: + type: str + description: + - Bot API method. + - For reference, see U(https://core.telegram.org/bots/api). + default: SendMessage + version_added: 2.0.0 + api_args: + type: dict + description: + - Any parameters for the method. + - For reference to default method, C(SendMessage), see U(https://core.telegram.org/bots/api#sendmessage). + version_added: 2.0.0 + msg: + type: str + description: + - (Deprecated) What message you wish to send. + msg_format: + type: str + description: + - (Deprecated) Message format. Formatting options C(markdown), C(MarkdownV2), and C(html) described in + Telegram API docs (https://core.telegram.org/bots/api#formatting-options). + If option C(plain) set, message will not be formatted. + default: plain + choices: [ "plain", "markdown", "MarkdownV2", "html" ] chat_id: type: str description: - - Telegram group or user chat_id - required: true + - (Deprecated) Telegram group or user chat_id. ''' EXAMPLES = """ -- name: Send a message to chat in playbook +- name: Send notify to Telegram + community.general.telegram: + token: '9999999:XXXXXXXXXXXXXXXXXXXXXXX' + api_args: + chat_id: 000000 + parse_mode: "markdown" + text: "Your precious application has been deployed: https://example.com" + disable_web_page_preview: True + disable_notification: True + +- name: Forward message to someone + community.general.telegram: + token: '9999999:XXXXXXXXXXXXXXXXXXXXXXX' + api_method: forwardMessage + api_args: + chat_id: 000000 + from_chat_id: 111111 + disable_notification: True + message_id: '{{ saved_msg_id }}' + +- name: Send a message to chat in playbook (deprecated old style) community.general.telegram: token: '9999999:XXXXXXXXXXXXXXXXXXXXXXX' chat_id: 000000 @@ -72,42 +108,59 @@ telegram_error: import json from ansible.module_utils.basic import AnsibleModule +# noinspection PyUnresolvedReferences from ansible.module_utils.six.moves.urllib.parse import quote from ansible.module_utils.urls import fetch_url def main(): - module = AnsibleModule( argument_spec=dict( token=dict(type='str', required=True, no_log=True), - chat_id=dict(type='str', required=True, no_log=True), - msg_format=dict(type='str', required=False, default='plain', - choices=['plain', 'markdown', 'html']), - msg=dict(type='str', required=True)), + api_args=dict(type='dict'), + api_method=dict(type="str", default="SendMessage"), + chat_id=dict(type='str', no_log=True, removed_in_version='4.0.0', + removed_from_collection='community.general'), + msg=dict(type='str', removed_in_version='4.0.0', removed_from_collection='community.general'), + msg_format=dict(type='str', choices=['plain', 'markdown', 'html', 'MarkdownV2'], default='plain', + removed_in_version='4.0.0', removed_from_collection='community.general'), + ), supports_check_mode=True ) token = quote(module.params.get('token')) - chat_id = quote(module.params.get('chat_id')) - msg_format = quote(module.params.get('msg_format')) - msg = quote(module.params.get('msg')) + api_args = module.params.get('api_args') or {} + api_method = module.params.get('api_method') + # filling backward compatibility args + api_args['chat_id'] = api_args.get('chat_id') or module.params.get('chat_id') + api_args['parse_mode'] = api_args.get('parse_mode') or module.params.get('msg_format') + api_args['text'] = api_args.get('text') or module.params.get('msg') - url = 'https://api.telegram.org/bot' + token + \ - '/sendMessage?text=' + msg + '&chat_id=' + chat_id - if msg_format in ('markdown', 'html'): - url += '&parse_mode=' + msg_format + if api_args['parse_mode'] == 'plain': + del api_args['parse_mode'] + + url = 'https://api.telegram.org/bot{token}/{api_method}'.format(token=token, api_method=api_method) if module.check_mode: module.exit_json(changed=False) - response, info = fetch_url(module, url) + response, info = fetch_url(module, url, method="POST", data=json.dumps(api_args), + headers={'Content-Type': 'application/json'}) if info['status'] == 200: module.exit_json(changed=True) + elif info['status'] == -1: + # SSL errors, connection problems, etc. + module.fail_json(msg="Failed to send message", info=info, response=response) else: body = json.loads(info['body']) - module.fail_json(msg="failed to send message, return status=%s" % str(info['status']), - telegram_error=body['description']) + module.fail_json( + msg="Failed to send message, return status = {status}\n" + "url = {api_url}\n" + "api_args = {api_args}".format( + status=info['status'], api_url=url, api_args=api_args + ), + telegram_error=body['description'], + ) if __name__ == '__main__':