Class: XNM::Telegram::GroupingAdapter

Inherits:
Object
  • Object
show all
Defined in:
lib/xnm/telegram/GroupingAdapter.rb

Overview

This class handles translating the sometimes a bit interesting Telegram API data to more usable types. It also handles the translation of User-IDs to the Usernames, and provides “Grouping IDs” to make it easier to edit, reply to, and delete messages It also exposes a much neater way of constructing inline keyboards.

Direct Known Subclasses

MQTT::Server

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(httpCore) ⇒ GroupingAdapter

Returns a new instance of GroupingAdapter.



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 19

def initialize(httpCore)
	# Check if we already have a HTTPCore, else create one
	@httpCore = if(httpCore.is_a? Telegram::HTTPCore)
			httpCore;
		else
			Telegram::HTTPCore.new(httpCore);
		end
	@httpCore.attach_receptor(self);

	_reset();
end

Instance Attribute Details

#groupIDListObject (readonly)

Returns the value of attribute groupIDList.



14
15
16
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 14

def groupIDList
  @groupIDList
end

#testLastDataObject (readonly)

Returns the value of attribute testLastData.



17
18
19
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 17

def testLastData
  @testLastData
end

#testLastUIDObject (readonly)

Returns the value of attribute testLastUID.



16
17
18
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 16

def testLastUID
  @testLastUID
end

#usernameListObject

Returns the value of attribute usernameList.



13
14
15
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 13

def usernameList
  @usernameList
end

Instance Method Details

#_handle_delete(data, uID) ⇒ Object

Deletes a message marked by a given GID

Parameters:

  • gid (String)

    The grouping-ID of the message to delete.

  • uID (Integer, String)

    The User-ID (as defined in @usernameList, or the raw ID), for which to delete.



166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 166

def _handle_delete(data, uID)
	# Resolve a saved Username to a User-ID
	uID = @usernameList[uID] if(@usernameList.key? uID)
	return if (uID = uID.to_i) == 0; # Return unless the username was known

	# Fetch the real message ID held by a grouping ID
	return unless mID = @groupIDList[uID][data]
	@groupIDList[uID].delete(data); # Clear that ID from the list

	# Perform the actual delete
	@httpCore.perform_post("deleteMessage", {chat_id: uID, message_id: mID});
end

#_handle_edit(data, uID) ⇒ Object

Edits an already known message. Takes arguments similar to _handle_send

Parameters:

  • data (Hash)

    The message to be edited. GID must be set, and optionally a inline keyboard markup or a new message text must be provided.

  • uID (Integer, String)

    The user-id as received from the MQTT Wildcard. Can be a username defined in @usernameList, or the raw Chat ID



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
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 133

def _handle_edit(data, uID)
	# Resolve a saved Username to a User-ID
	uID = @usernameList[uID] if(@usernameList.key? uID)
	return if (uID = uID.to_i) == 0; # Return if a unknown Username was used

	# Fetch the target MessageID - Return if none is present
	return unless mID = @groupIDList[uID][data[:gid]]

	# Lay out all mandatory arguments for the edit POST
	outData = {
		chat_id: uID,
		message_id: mID,
	};

	# If a inline keyboard was given, parse that.
	if(ilk = data[:inline_keyboard])
		outData[:reply_markup] = _process_inline_keyboard(ilk, data[:gid]);
	end

	if(data[:text]) # Check if text was given
		outData[:text] = data[:text];
		# Send the POST request editing the message text
		@httpCore.perform_post("editMessageText", outData);
	else
		# Otherwise, only edit the reply markup (keyboard etc.)
		@httpCore.perform_post("editMessageReplyMarkup", outData);
	end
end

#_handle_send(data, uID) ⇒ Object

Processes messages received through MQTT/Custom input It takes care of setting a few good defaults (like parse_mode), deletes any old messages of the same GroupID (if requested), and stores the new Message ID for later processing Tested in ts_mqtt/test_send

Parameters:

  • data (Hash)

    The message packet to be sent to Telegram The “text” field is always required. Optional fields are:

    • gid: Grouping-ID for later editing/deleting/replying to

    • replace: true/false whether or not the old GID-Tagged message should be deleted

    • overwrite: Similar to replace, but instead of re-sending, it only edits the old message

    • silent: Sets the “disable_notification” flag

    • inline_keyboard: Hash of inline keyboard buttons (Button-text as key, button reply as value)

  • uID (Integer, String)

    The user-id as received from the MQTT Wildcard. Can be a username defined in @usernameList, or the raw Chat ID



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
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 86

def _handle_send(data, uID)
	# Resolve a saved Username to a User-ID
	uID = @usernameList[uID] if(@usernameList.key? uID)
	return if (uID = uID.to_i) == 0; # Return if a unknown Username was used

	gID = data[:gid];

	# Check if a GroupID is present and a former message is known
	if(gID and @groupIDList[uID][gID])
		if(data[:replace])
			_handle_delete(gID, uID)
		elsif(data[:overwrite])
			_handle_edit(data, uID);
			return; # After editing, no new message should be sent!
		end
	end

	# Lay out all mandatory parameters for sendMessage request
	outData = {
		chat_id: 	uID,
		parse_mode: (data[:parse_mode] or "Markdown"), # Markdown parse mode is just nice
		text:			data[:text]
	}

	# Check if the message is meant to be sent without notification
	if(data[:silent])
		outData[:disable_notification] = true;
	end

	# Check if an inline keyboard layout is given, and parse that.
	if((ilk = data[:inline_keyboard]))
		outData[:reply_markup] = _process_inline_keyboard(ilk, gID);
	end

	reply = @httpCore.perform_post("sendMessage", outData);
	return unless reply[:ok]	# Something was wrong about our message layout
	# TODO Add a proper error handler here.

	# If a GroupID was given, save the sent message's ID
	@groupIDList[uID][gID] = reply[:result][:message_id] if(gID);
end

#_process_inline_keyboard(keyboardLayout, gID = nil) ⇒ Object

Tested in ts_group_adapter/test_keyboard_build



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
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 41

def _process_inline_keyboard(keyboardLayout, gID = nil)
	# Return unless we have a structure we can form into a keyboard
	return nil unless (keyboardLayout.is_a? Array or keyboardLayout.is_a? Hash)

	# Make sure the structure of keyboardLayout is [{}] or [[]]
	if(keyboardLayout.is_a? Hash)
		keyboardLayout = [keyboardLayout]
	elsif(not (keyboardLayout[0].is_a? Array or keyboardLayout[0].is_a? Hash))
		keyboardLayout = [keyboardLayout]
	end

	outData = Array.new();

	# Iterate through the rows of keyboards
	keyboardLayout.each do |row|
		newRow = Array.new();

		# Create the INLINE KEY button elements
		row.each do |key, val|
			cbd = {i: gID, k: (val or key)};
			newRow << {text: key, callback_data: cbd.to_json}
		end

		# Add the new row to the array of rows
		outData << newRow;
	end

	# Return the ready reply_markup element
	return {inline_keyboard: outData};
end

#_resetObject



31
32
33
34
35
36
37
38
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 31

def _reset()
	# Hash {username => ChatID}
	@usernameList 	= Hash.new();
	# Hash {ChatID => {GroupID => MessageID}}
	@groupIDList 	= Hash.new do |hash, key|
		hash[key] = Hash.new;
	end
end

#handle_callback_query(cbq) ⇒ Object

Handle an incoming callback query (inline keyboard button press) as received from the HTTP Core



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 228

def handle_callback_query(cbq)
	# Send out a callback query reply (i.e. Telegram now knows we saw it)
	@httpCore.perform_post("answerCallbackQuery", {callback_query_id: cbq[:id]});

	# Resolve the username, if we know it.
	uID = msg[:message][:chat][:id];
	if(newUID = @usernameList.key(uID))
		uID = newUID
	end

	# Try to parse the data. This gem sets inline keyboard reply data to
	# a small JSON, which identifies the GID and the key that was pressed.
	begin
		data = JSON.parse(cbq[:data], symbolize_names: true);
	rescue
		return;
	end

	data = {
		gid:  data[:i],
		key: data[:k],
	}

	# If the key ID starts with a command slash, treat it like a normal
	# command. Has the benefit of making it super easy to execute already
	# implemented actions as a inline keyboard
	if(data[:key] =~ /^\//)
		on_command({text: data[:key], gid: data[:gid]}, uID);
	end
	on_callback_pressed(data, uID);
end

#handle_message(msg) ⇒ Object

Handle an incoming message packet from the HTTP core



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 195

def handle_message(msg)
	uID = msg[:chat][:id];
	# Resolve the User-ID, if it's known.
	if(newUID = @usernameList.key(uID))
		uID = newUID
	end

	data = Hash.new();
	# Only accept messages that contain text (things like keyboard replies
	# are handled elsewhere).
	return unless(data[:text] = msg[:text])

	# See if this message was a reply, and if we know said reply under a group-id
	if(replyMSG = msg[:reply_to_message])
		data[:reply_gid] = @groupIDList[uID].key(replyMSG[:message_id]);
	end

	# Distinguish the type of message. If it starts with a command-slash,
	# it will be excempt from normal processing.
	# If it has a reply message ID that we know, handle it as a reply.
	# Otherwise, simply send it off as a normal message.
	if(data[:text] =~ /^\//)
		on_command(data, uID)
	elsif(data[:reply_gid])
		on_reply(data, uID)
	else
		on_message(data, uID)
	end
end

#handle_packet(packet) ⇒ Object

Handle incoming HTTP Core packets. Just send them to the appropriate handler function



262
263
264
265
266
267
268
269
270
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 262

def handle_packet(packet)
	if(msg = packet[:message])
		handle_message(msg);
	end

	if(cbq = packet[:callback_query])
		handle_callback_query(cbq);
	end
end

#on_callback_pressed(data, uID) ⇒ Object



190
191
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 190

def on_callback_pressed(data, uID)
end

#on_command(data, uID) ⇒ Object



184
185
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 184

def on_command(data, uID)
end

#on_message(data, uID) ⇒ Object



179
180
181
182
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 179

def on_message(data, uID)
	@testLastUID  = uID;
	@testLastData = data;
end

#on_reply(data, uID) ⇒ Object



187
188
# File 'lib/xnm/telegram/GroupingAdapter.rb', line 187

def on_reply(data, uID)
end