-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathchatbot.py
188 lines (154 loc) · 5.9 KB
/
chatbot.py
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
187
188
# element.io SDK
from matrix_client.client import MatrixClient, MatrixRequestError
from requests.exceptions import MissingSchema
import re
from enum import Enum
import matrix_client.client
import logging
logger = logging.getLogger(__name__)
# ###
# # usage:
#
# from chatbot import Chatbot
# bot = Chatbot(bot_conf.get('host'), bot_conf.get('user'), bot_conf.get('pass'), bot_conf.get('room'))
# bot.talk("Hello World")
# bot.logout()
#
# ###
# more info https://matrix-org.github.io/matrix-python-sdk/matrix_client.html
class MessageType(Enum):
NOTICE = 'notice'
TEXT = 'text'
HTML = 'html'
class Chatbot:
"""Simplified Matrix Chatbot"""
def __init__(self, host, room, username=None, password=None, userid=None, token=None):
if (username or password) and (userid or token):
raise Exception(
"You should either specify username and password " +
"for login authentication, or userid and token for token " +
"authentication, but not both.")
if username and not password:
raise Exception("Missing password")
if userid and not token:
raise Exception("Missing token")
if not username and not userid:
raise Exception("No authentication parameters specified")
# keep false to preserve token.
self.logout_on_exit = False
try:
# connect with server
self.client = MatrixClient(host, user_id=userid, token=token)
logger.info("bot connected with host: {}".format(host))
logger.info("matrix_client.client.ENCRYPTION_SUPPORT: {}".format(matrix_client.client.ENCRYPTION_SUPPORT))
if username:
self.client.login(username, password)
self.logout_on_exit = True
logger.info("bot connected as user: {}".format(username))
except MatrixRequestError as e:
print("MatrixRequestError:", e)
if e.code == 403:
logger.info("Bad username or password.")
# sys.exit(4)
else:
logger.info("Check your sever details are correct.")
# sys.exit(2)
raise(e)
except MissingSchema as e:
logger.info("Bad URL format.")
print("DEBUG:", e)
# sys.exit(3)
raise(e)
# join chat room
try:
self.room = self.client.join_room(room)
except MatrixRequestError as e:
print("MatrixRequestError:", e)
if e.code == 400:
logger.info("Room ID/Alias in the wrong format")
# sys.exit(11)
else:
logger.info("Couldn't find room.")
# sys.exit(12)
raise(e)
logger.info("bot connected to room: {}".format(room))
# #room callback:
# # self.room.add_listener(lambda room, event : print("CALLBACK room.add_listener => room, event:", room, event, "\n", self.print_event(event)))
# self.client.add_listener(lambda event : print("CALLBACK client.add_listener => event:", event, "\n", self.print_event(event)))
#
# # Parameters: callback (func(room_id, state) – Callback called when an invite arrives
# self.client.add_invite_listener(lambda room_id, state: print("CALLBACK add_invite_listener => room_id, state:", room_id, state, "\n"))
# self.client.start_listener_thread()
#
#
# def print_event(self,event):
# print("debug: type(event)", type(event))
# print("debug: event['content']['body']: ", event.get('content', {}).get('body', None))
#
# if event['type'] == "m.room.member":
# if event['membership'] == "join":
# print("{0} joined".format(event['content']['displayname']))
# elif event['type'] == "m.room.message":
# if event['content']['msgtype'] == "m.text":
# print("{0}: {1}".format(event['sender'], event['content']['body']))
# else:
# print(event['type'])
def talk(self, body=None, body_html=None, notice=False):
"""talk message in any type"""
# log msg line:
logger.info("talk: body={}, body_html={}, notice={})".format(repr(body), repr(body_html), notice))
return self.send_message(body=body, body_html=body_html, notice=notice)
def update_talk(self, event_id, new_body=None, body=None, body_html=None, new_body_html=None, room_id=None, notice=False):
"""Update existing message.
a bit reversed engineerd but seems to work.
"""
logger.info("update: new_body={} new_body_html={} body={} body_html={} notice={})".format(repr(new_body), repr(body_html), repr(body), repr(body_html), notice))
return self.send_message(event_id=event_id, body=body, new_body=new_body, body_html=body_html, new_body_html=new_body_html, room_id=room_id, notice=notice)
def send_message(self, body=None, new_body=None, body_html=None, new_body_html=None, event_id=None, room_id=None, notice=False):
# set our own room id if not defined.
if not room_id:
room_id = self.room.room_id
content = {}
if notice:
content['msgtype'] = 'm.notice'
else:
content['msgtype'] = 'm.text'
# add body of the update (for new messages, and for clients that do
# not understand edits)
if not body and body_html:
body = re.sub('<[^<]+?>', '', body_html)
elif not body:
raise Exception("body or body_html is required")
content['body'] = body
if body_html:
content['format'] = "org.matrix.custom.html"
content['formatted_body'] = body_html
if event_id:
if not new_body and new_body_html:
new_body = re.sub('<[^<]+?>', '', new_body_html)
elif not new_body:
raise Exception("new_body is required for updates")
# This is the actual updated message as rendered by clients that
# do understand edits
content['m.new_content'] = {
'msgtype': content['msgtype'],
'body': new_body,
}
if new_body_html:
content['m.new_content']['format'] = "org.matrix.custom.html"
content['m.new_content']['formatted_body'] = new_body_html
content['m.relates_to'] = {
'rel_type': 'm.replace',
'event_id': event_id,
}
else:
if new_body or new_body_html:
raise Exception("new_body and new_body_html only valid for updates")
return self.client.api.send_message_event(room_id,'m.room.message', content)
def logout(self):
if self.logout_on_exit:
self.client.logout()
logger.info("bot logged out.")
else:
logger.info("bot token is preserved.")
# vim: set noet ts=4 sw=4: