class XNM::Telegram::GroupingAdapter

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.

Attributes

groupIDList[R]
testLastData[R]
testLastUID[R]
usernameList[RW]

Public Class Methods

new(httpCore) click to toggle source
# 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

Public Instance Methods

_handle_delete(data, uID) click to toggle source

Deletes a message marked by a given GID @param gid [String] The grouping-ID of the message to delete. @param uID [Integer, String] The User-ID (as defined in @usernameList, or

the raw ID), for which to delete.
# 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) click to toggle source

Edits an already known message. Takes arguments similar to _handle_send @param 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.

@param uID [Integer,String] The user-id as received from the MQTT Wildcard.

Can be a username defined in @usernameList, or the raw Chat ID
# 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) click to toggle source

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 @param 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)

@param uID [Integer,String] The user-id as received from the MQTT Wildcard.

Can be a username defined in @usernameList, or the raw Chat ID

Tested in ts_mqtt/test_send

# 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) click to toggle source

Tested in ts_group_adapter/test_keyboard_build

# 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
_reset() click to toggle source
# 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) click to toggle source

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

# 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) click to toggle source

Handle an incoming message packet from the HTTP core @private

# 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) click to toggle source

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

# 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) click to toggle source
# File lib/xnm/telegram/GroupingAdapter.rb, line 190
def on_callback_pressed(data, uID)
end
on_command(data, uID) click to toggle source
# File lib/xnm/telegram/GroupingAdapter.rb, line 184
def on_command(data, uID)
end
on_message(data, uID) click to toggle source
# File lib/xnm/telegram/GroupingAdapter.rb, line 179
def on_message(data, uID)
        @testLastUID  = uID;
        @testLastData = data;
end
on_reply(data, uID) click to toggle source
# File lib/xnm/telegram/GroupingAdapter.rb, line 187
def on_reply(data, uID)
end