Script: irssinotifier.py

IrssiNotifier push notifications with end-to-end encryption.
Author: Caspar Clemens Mierau — Version: 0.9 — License: GPL-3.0-or-later
For WeeChat ≥ 0.3.7, requires: openssl.
Tags: irssi, notify, py2, py3
Added: 2013-01-18 — Updated: 2018-11-11

Download GitHub Repository

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# -*- coding: utf-8 -*-
# Author: Caspar Clemens Mierau <ccm@screenage.de>
# Homepage: https://github.com/leitmedium/weechat-irssinotifier
# Derived from: notifo
#   Author: ochameau <poirot.alex AT gmail DOT com>
#   Homepage: https://github.com/ochameau/weechat-notifo
# And from: notify
#   Author: lavaramano <lavaramano AT gmail DOT com>
#   Improved by: BaSh - <bash.lnx AT gmail DOT com>
#   Ported to Weechat 0.3.0 by: Sharn - <sharntehnub AT gmail DOT com)
# And from: notifo_notify
#   Author: SAEKI Yoshiyasu <laclef_yoshiyasu@yahoo.co.jp>
#   Homepage: http://bitbucket.org/laclefyoshi/weechat/
#
# This plugin brings IrssiNotifier to your Weechat. Setup and install
# IrssiNotifier first: https://irssinotifier.appspot.com
#
# Requires Weechat >= 0.3.7, openssl
# Released under GNU GPL v3
#
# 2018-10-02, Pol Van Aubel <dev@polvanaubel.com>
#     version 0.9: - Make python3-compatible.
# 2017-05-17, das_aug <wct@fnanp.in-ulm.de>
#     version 0.8.1 - change openssl commandline to how the android app uses it now
#                     (add "-md md5")
# 2017-05-11, paalka <paal@128.no>
#     version 0.8: - add the ability to store the API token and
#                    encryption key as secured data.
# 2016-01-11, dbendit <david@ibendit.com>
#     version 0.7: - ignore_nicks option
# 2014-05-10, Sébastien Helleu <flashcode@flashtux.org>
#     version 0.6.3: - change hook_print callback argument type of
#                      displayed/highlight (WeeChat >= 1.0)
# 2013-12-07, zigdon
#     version 0.6.2: - support ignoring all buffers in a server, add help text.
# 2013-08-20, balu
#     version 0.6.1: - support for every private notification not only irc (especialy also jabber)
# 2013-08-16, kang@insecure.ws
#     version 0.6: - only_away option (only notify if set away)
# 2013-01-18, ccm <ccm@screenage.de>:
#     version 0.5: - removed version check and legacy curl usage
# 2012-12-27, ccm <ccm@screenage.de>:
#     version 0.4: - use non-blocking hook_process_hashtable for url call
#                    for weechat >= 0.3.7
# 2012-12-22, ccm <ccm@screenage.de>:
#     version 0.3: - no longer notifies if the message comes from the user
#                    itself
#                  - removed curl dependency
#                  - cleaned up openssl call
#                  - no more crashes due to missing escaping
#                  - Kudos to Juergen "@tante" Geuter <tante@the-gay-bar.com>
#                    for the patches!
# 2012-10-27, ccm <ccm@screenage.de>:
#     version 0.2: - curl uses secure command call (decreases risk of command
#                    injection)
#                  - correct split of nick and channel name in a hilight
# 2012-10-26, ccm <ccm@screenage.de>:
#     version 0.1: - initial release - working proof of concept

import weechat, os, shlex
from subprocess import Popen, PIPE

import sys # Only required for python version check.
PY3 = not sys.version_info < (3,)

if PY3:
    from urllib.parse import urlencode
else:
    from urllib import urlencode

weechat.register("irssinotifier",
                 "Caspar Clemens Mierau <ccm@screenage.de>",
                 "0.9",
                 "GPL3",
                 "irssinotifier: Send push notifications to Android's IrssiNotifier about your private message and highligts.",
                 "",
                 "")

settings = {
    "api_token": "API token from http://irssinotifier.appspot.com.",
    "encryption_password": "Your password, same as on the phone's client.",
    "only_away": "Only send notifications when set as away.",
    "ignore_buffers": "Comma separated list of buffers to ignore.",
    "ignore_servers": "Comma separated list of servers to ignore.",
    "ignore_nicks": "Comma separated list of nicks to ignore.",
}

required_settings = ["api_token", "encryption_password"]

for option, help_text in list(settings.items()):
    if not weechat.config_is_set_plugin(option):
        weechat.config_set_plugin(option, "")

    if option in required_settings and weechat.config_get_plugin(option) == "":
        weechat.prnt("", weechat.prefix("error") + "irssinotifier: Please set option: %s" % option)
        weechat.prnt("", "irssinotifier: /set plugins.var.python.irssinotifier.%s STRING" % option)

    weechat.config_set_desc_plugin(option, help_text)

# Hook privmsg/hilights
weechat.hook_print("", "notify_message", "", 1, "notify_show", "")
weechat.hook_print("", "notify_private", "", 1, "notify_show", "")

# Functions
def notify_show(data, bufferp, uber_empty, tagsn, isdisplayed,
        ishilight, prefix, message):

    # irc PMs are caught by notify_private, but we need notify_message to
    # capture hilights in channels.
    if 'notify_message' in tagsn and not ishilight:
        return weechat.WEECHAT_RC_OK

    # are we away?
    away = weechat.buffer_get_string(bufferp,"localvar_away")
    if (away == "" and weechat.config_get_plugin("only_away") == "on"):
        return weechat.WEECHAT_RC_OK

    # get local nick for buffer
    mynick = weechat.buffer_get_string(bufferp,"localvar_nick")

    # get buffer info
    name = weechat.buffer_get_string(bufferp,"name")
    server = weechat.buffer_get_string(bufferp, "localvar_server")
    channel = weechat.buffer_get_string(bufferp, "localvar_channel")

    # ignore buffers on ignorelists
    if not (server in weechat.config_get_plugin("ignore_servers").split(",") or
        name in weechat.config_get_plugin("ignore_buffers").split(",") or
        prefix in weechat.config_get_plugin("ignore_nicks").split(",")):

        # only notify if the message was not sent by myself
        if (weechat.buffer_get_string(bufferp, "localvar_type") == "private") and (prefix!=mynick):
            show_notification(channel, prefix, message)

        elif int(ishilight):
            buffer = (weechat.buffer_get_string(bufferp, "short_name") or name)
            show_notification(buffer, prefix, message)

    return weechat.WEECHAT_RC_OK

def encrypt(text):
    encryption_password = weechat.config_get_plugin("encryption_password")

    # decrypt the password if it is stored as secured data
    if encryption_password.startswith("${sec."):
        encryption_password = weechat.string_eval_expression(encryption_password, {}, {}, {})

    if PY3:
        text = text.encode("UTF-8")

    command="openssl enc -aes-128-cbc -salt -base64 -md md5 -A -pass env:OpenSSLEncPW"
    opensslenv = os.environ.copy();
    # Unknown whether the encryption password should or should not be
    # (UTF8-)encoded before being passed to the environment in python 3.
    opensslenv['OpenSSLEncPW'] = encryption_password
    output, errors = Popen(shlex.split(command), stdin=PIPE, stdout=PIPE,
                           stderr=PIPE,env=opensslenv).communicate(text + b" ")
    output = output.replace(b"/", b"_")
    output = output.replace(b"+", b"-")
    output = output.replace(b"=", b"")

    if PY3:
        output = output.decode("UTF-8")

    return output

def show_notification(chan, nick, message):
    API_TOKEN = weechat.config_get_plugin("api_token")

    # decrypt the API token if it is stored as secured data
    if API_TOKEN.startswith("${sec."):
        API_TOKEN = weechat.string_eval_expression(API_TOKEN, {}, {}, {})

    if API_TOKEN != "":
        url = "https://irssinotifier.appspot.com/API/Message"
        postdata = urlencode({'apiToken' : API_TOKEN,
                              'nick'     : encrypt(nick),
                              'channel'  : encrypt(chan),
                              'message'  : encrypt(message),
                              'version'  : 13})
        version = weechat.info_get("version_number", "") or 0
        hook1 = weechat.hook_process_hashtable("url:" + url,
                                               {"postfields": postdata},
                                               2000, "", "")

# vim: autoindent expandtab smarttab shiftwidth=4