Post list
LibraBot, a very simple bot for Matrix

Dear Brothers and Sisters of the Eternal FOSS, today we will take a look at how to write a simple bot for Matrix. But what is Matrix? Are we talking about the modern term for maya, the Hindu word for "that which is not", i.e. the illusion, the entanglement suffered by the atman or soul when identified with the physical body and its consequent materialistic ramifications? In fact we don't, since we are actually referring to Matrix, an open-source network for secure, decentralized communication.

Our current project, called LibraBot, is built on top of python-matrix-bot-api, a handy wrapper for the matrix-python-sdk, and includes a few REST functions imported from our previous Mastodon bots plus a new method to retrieve gifs from Tenor. These functions are then linked to command handlers, so that whenever a user in the room invokes a command such as !command, our bot will run the specified callback function. Mind that our bot needs to be invited to the room beforehand! There is just one detail: the room.send_image() function does not accept an image, but is expecting an MXC URL, which is returned after uploading an image to the Matrix server, therefore we need to upload the image before sending it to the room. The code is as follows:

import random, requests, json, os, magic, pathlib
from matrix_bot_api.matrix_bot_api import MatrixBotAPI
from matrix_bot_api.mregex_handler import MRegexHandler
from matrix_bot_api.mcommand_handler import MCommandHandler
from matrix_client.client import MatrixClient

# Load credentials from JSON file
cred = json.load(open("credentials.json"))
client = MatrixClient(cred["server"])
token = client.login(username=cred["username"], password=cred["password"])

# Save PNG from URL, upload it to the server and send the image MCX URL to the room
def send_image_from_url(room, url):
    image = requests.get(url).content
    with open("temp.png", "wb") as png:
        png.write(image)
    mime_type = magic.from_file("temp.png", mime=True)
    mxc = client.upload(image, mime_type)
    room.send_image(mxc,"image")
    os.remove("temp.png")

# Send a random cat picture
def cat_callback(room, event):
    room.send_text("Serving a cat, please wait...")
    send_image_from_url(room, json.loads(requests.get('http://aws.random.cat/meow').content)["file"])

# Send a random dog picture
def dog_callback(room, event):
    room.send_text("Serving a dog, please wait...")
    send_image_from_url(room, json.loads(requests.get('https://random.dog/woof.json').content)["url"])

# Send a random fox picture
def fox_callback(room, event):
    room.send_text("Serving a fox, please wait...")
    send_image_from_url(room, json.loads(requests.get('https://randomfox.ca/floof/').content)["image"])

# Send a random gif
def gif_callback(room, event):
    room.send_text("Serving a gif, please wait...")
    args = event['content']['body']
    arg = args[5:]
    if(arg == ""):
        room.send_text("You need to provide an argument! Example: !gif happy")
        return
    send_image_from_url(room, json.loads(requests.get("https://g.tenor.com/v1/search?key="
    + cred["tenorAPI"] + "&limit=1&q=" + arg).content)["results"][0]["media"][0]["gif"]["url"])

def main():

    bot = MatrixBotAPI(cred["username"], cred["password"], cred["server"])

    # Set command handlers
    bot.add_handler(MCommandHandler("cat", cat_callback))
    bot.add_handler(MCommandHandler("dog", dog_callback))
    bot.add_handler(MCommandHandler("fox", fox_callback))
    bot.add_handler(MCommandHandler("gif", gif_callback))

    bot.start_polling()
    while True:
        input()

if __name__ == "__main__":
    main()

This has been post #7 in the #100DaysToOffload challenge. As always, thank you for reading and see you next time.


Bot galore!

Tonight, right before your screen, transmitting from this abstract location of the wild lands of the Internet we leave aside our previous psychological explorations and come back to what's really important, which is as you know to populate the fediverse with artificial intelligence nobody asked for in order to create a rich environment for the emergence of synchronicities among other interesting phenomena that sweet randomness entails.

Thanks to my increasing interest in this field I quickly realized there is a pletora of REST APIs available in the Internet for everyone to use. As we saw with Meowbot, the process is really simple. We just need to send a GET verb to the URL the API provides, and in return we get a JSON file that we can disengage into data pieces at our will.

Let's meet our first contestant of the night. His name is Poeticus and his honor/duty is to share random fragments of poetry with the fediverse thanks to the API that Poemist provides. Now the only problem here was that Poemist would only return a single verse plus a URL to the Poemist poem page with clear intent to generate traffic to its own website. This situation was certainly unsatisfying, since the poetic fedivexperience we wanted to achieve had to be self-contained and not requering to jump to an external site. So this time we have been a little sneaky and deployed a tiny spider which scraps the delicious content that we want, only for educational purposes of course. In order to do that, the Scrapy library includes a spider class that we will extend with the rest of our code. Let's take a look:

import scrapy, json, requests
from mastodon import Mastodon

class spider(scrapy.Spider):
    name = "Poeticus"
    title = ""
    poet = ""

    def __init__(self):
        # Get JSON
        poem = json.loads(requests.get("https://www.poemist.com/api/v1/randompoems").content)[0]

        # Store title, author and the URL to Poemist page
        self.title = poem["title"]
        self.poet = poem["poet"]["name"]
        self.start_urls = [poem["url"]]

    def parse(self, response):

        # Mastodon token and domain
        mastodon = Mastodon(
            access_token = "asdf",
            api_base_url = "https://domain.com/"
        )

        # Create the string that will be sent to Mastodon and add title and author
        string = self.title.strip() + " by " + self.poet.strip() + "\n\n"

        # Get the poem which is contained in the <div class="poem-content"> tag at the Poemist page
        imported = response.css('.poem-content::text')
        text = imported.getall()

        # Iterate through all lines and add them to our string
        # till we get close to Mastodon's 500 character limit
        # We get rid of empty and weird lines and strip them to tidy the result
        for i in range(0, len(string)):
            if(len(string) < 450 and text[i] != "\n" and text[i] != "\n\n"
            and text[i] != "\n\n\n" and text[i] != "I\n"
            and text[i] != "II\n" and text[i] != "*\n"):
                string += text[i].strip() + "\n"

        # Send string to Mastodon
        mastodon.status_post(string)

That is all. To run it we will execute in the terminal...

scrapy runspider ourfile.py

...and a quite expressive log will great us, hopefully showing that our spider did fine!

Before we continue this exciting journey to nowhere, we will now experience a regression to an older and simpler time, back to when I was still kept captive in a so-called "high-school" receiving the mandatory mental programming as any person of my age would be back then. Lessons from different subjects were held by unstable teachers in buildings that resemble prisons. At the center of it all there was the high-scholl bell, an omnipresent, all-pervasive entity that ruled our lives and determined our thought schedule.

During mid-morning, this almigthy bell was pious enough to grant us half hour of break from the brainwashing, time that I would use to absorb some of that sunlight that was otherwise denied during those classes about nothing. It was during one of those breaks when my friend approached me holding a deck of mysterious-looking cards. It was the moment Magic The Gathering entered my life. Was that a significant moment, you may be asking yourself? Well no, it was not. I couldn not care less about a game about cards that had to be purchased with money I did not have. There was only one aspect that captivated my mind, and that was the killer artwork that each card had. This was the inspiration for our next contestant MagicBot, a bot that shares Magic The Gathering artwork thanks to Scryfall's API. Since the source code is basically identical to Meowbot's, it is somewhat redundant to show it here, and instead a Github link is provided.

Leaving magic and cats aside, we will reach the end of our session with my personal attempt at creating something original: meet Doggobot, a bot that shares dog pictures, courtesy of dog.ceo. Now the universal equilibrium is cosmically restored once again. The code can also be found in Github.

This is post #6 in the #100DaysToOffload challenge. As always, thank you for reading and see you next time?


What is wetiko?

Wetiko is an Algonquin word for a cannibalistic spirit that is driven by greed, excess, and selfish consumption (in Ojibwa it is windigo, wintiko in Powhatan). It deludes its host into believing that cannibalizing the life-force of others (others in the broad sense, including animals and other forms of Gaian life) is a logical and morally upright way to live.

Wetiko short-circuits the individual's ability to see itself as an enmeshed and interdependent part of a balanced environment and raises the self-serving ego to supremacy. It is this false separation of self from nature that makes this cannibalism, rather than simple murder. It allows —indeed commands— the infected entity to consume far more than it needs in a blind, murderous daze of self-aggrandizement. Author Paul Levy, in an attempt to find language accessible for Western audiences, describes it as "malignant egophrenia"—the ego unchained from reason and limits, acting with the malevolent logic of the cancer cell.

A wetiko-free psyche has woke up to the existence of the wetiko pathogen. Turned onto wetiko's nonlocal and shape-shifting nature, both as it plays out in the world and within ourselves, we become aware of the very real tendency within ourselves of self-decepcion, on how we all have the potential to fool ourselves via the creative power of our own mind. This realization of our potential susceptibility to self-deception, which could lead to unwittingly becoming instruments for the devil of wetiko to act itself out through us, serves as a psychihc immunization, inculcating a true humility that safeguards against evil. Everyone, including ourselves, has the potentiality for falling into —and acting out— the unconscious. Because of our awareness of the possibility of pulling the wool over our own eyes, a relatively wetiko-free person cultivates on a daily basis the practice of mindfulness, which serves as a guardian of the gates of our psyche. In addition, to use religious terminology, because we are aware of our potential weakness and yetzer ha-ra (Hebrew for the "evil inclination" within us), we develop a relationship with and rely upon a "higher power" beyond our own limited ego, whether we call it God, the Self, our daemon, our true nature, or whichever of the thousands of names by which it is called. This is very different from when we are afflicted with wetiko, as we are then unconsciously identified with this higher power, which is the very stance which allows us to get away with murder.

For more information on wetiko, you can grab a copy of Dispelling Wetiko by Paul Levy, a very recommendable reading in these dark times of noise and confusion. This is post #5 in the #100DaysToOffload challenge. As always, thanks for reading and see you next time.

Previous page
Next page