I'm using "mqttasgi" as library with Django justo to listen and posting many messages. However, for some reason after a few days it is no longer possible to continue posting messages. It should be noted that I am using the amazon login with "mqttasgi" and a level 1 of QO(because AWS doesn't allow a level 2 of QO).
This is my procfile
mqttasgi -H $MQTT_URL -p $MQTT_PORT -v 2 -C $TLS_CERT -K $TLS_KEY -S $TLS_CA iot_stracontech.asgi:application```
and this is my consumer.py
from mqttasgi.consumers import MqttConsumer
from mqtt_handler.tasks import processmqttmessage
import json
class MyMqttConsumer(MqttConsumer):
async def connect(self):
await self.subscribe('tpx/things/+/uplink', 0)
await self.channel_layer.group_add("stracontech", self.channel_name)
async def receive(self, mqtt_message):
print('Received a message at topic:', mqtt_message['topic'])
print('With payload', mqtt_message['payload'])
print('And QOS:', mqtt_message['qos'])
dictresult = json.loads(mqtt_message['payload'])
jsonresult = json.dumps(dictresult)
processmqttmessage.delay(jsonresult, mqtt_message['topic'])
pass
async def publish_results(self, event):
data = event['result']
await self.publish("stracontech/procesed/" + event['result']['device_id'] + "/result",
json.dumps(data).encode('utf-8'), qos=1, retain=False)
async def disconnect(self):
await self.unsubscribe('tpx/things/+/uplink')
I want to know if exist a way to know why does it stop publishing messages, anyway to do a debug or see the logs?
Pd: #Santiago Ivulich maybe you can give me a hand with that.
Related
I'm trying to build a discord bot that sends new twitter posts into chat (in realtime). Not all that familiar to async yet, and with minimal examples available I'm still kind of lost.
I have two classes, twitterClient and discordClient, and I pass the message sending function and the event loop to the twitterClient. I then call asyncio.run_coroutine_threadsafe to run the message sending function when the twitterClient receives a tweet.
When I run this and post a tweet, the program seems to receive the tweet (as shown by the print statement) but the program doesn't get any further. Since ctrl-c doesn't work after this step, it seems to me that the program freezes there. What should I do? Am I even using asyncio correctly?
import discord
from discord.ext import commands
import tweepy
import asyncio
import os
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
BEARER = os.getenv('TWITTER_BEARER_TOKEN')
rule = {'fromknowgnod':'from:knowgnod'}
class twitterClient(tweepy.StreamingClient):
def __init__(self, send, loop, *args, **kwargs):
super().__init__(*args, **kwargs)
self.loop = loop
self.send = send
def on_connect(self):
print('connected to stream')
# check if rule already uploaded
api_rule_tags = [i.tag for i in self.get_rules().data]
if list(rule.keys())[0] not in api_rule_tags:
self.add_rule(value=rule[list(rule.keys())[0]], tag=list(rule.keys())[0])
def on_tweet(self, tweet):
print("received tweet: "+str(tweet.id))
a = asyncio.run_coroutine_threadsafe(self.send(tweet.id), self.loop)
a.result()
def on_errors(self, errors):
print(f"Received error code {errors}")
self.disconnect()
return False
class discordClient(discord.Client):
async def on_ready(self):
print(f'Logged on as {self.user}!')
myStream = twitterClient(self.send_message, asyncio.get_event_loop(), BEARER)
myStream.filter()
async def send_message(self, msg):
c = self.get_channel(1003516355003289694)
c.send(f'https://twitter.com/knowgnod/status/{msg}')
intents = discord.Intents.default()
intents.messages = True
client = discordClient(intents=intents)
client.run(TOKEN)
Am trying to create websocket using django channels and integrate mqtt with channels and mqtt publish message should be received by the function inside consumer.py should be sent to websocket client.
I have consumer channel like below
consumer.py
from channels.consumer import AsyncConsumer
from paho.mqtt import client as Mqtt
class Testing(AsyncConsumer):
async def websocket_connect(self, event):
obj = Mqtt.Client()
obj.connect("localhost", 1883, 60)
obj.on_message = self.updater
obj.subscribe("Testing")
obj.loop_start()
async def updater(self, arg1, arg2, message):
print(message)
await self.send({
"type": "websocket.send",
"text": message})
async def websocket_receive(self, text_data):
pass
In the above mqtt connection has happened but if I publish a message to the topic its not working. updater function inside consumer.py is not being called. How to achieve this ?
After coming here looking for help on this topic myself I created chanmqttproxy
Essentially it's a fully async Channels 3 proxy to MQTT that allows publishing and subscribing. The documentation show how to extend the standard Channels tutorial so chat messages are seen on MQTT topics - and can be sent from MQTT topics to all websocket browser clients.
I looked at MQTTAsgi (and credited it for inspiration) but felt that using a pure async solution with full Channels integration was more appropriate.
I'm trying to get the last message from X GUILD and Y CHANNEL that arrives in discord only to read it and print it into Phyton console but it's so confusing, there are tokens for bots and also the official API and it's a bit difficult for me to get through all it alone with my current knowledge level.
So, here's the code I've right now.
It's difficult for me to understand sync and async functions too.
import discord
import asyncio
client = discord.Client()
async def get_message(CHANNEL_ID):
msg = await client.get_channel(CHANNEL_ID).history(limit=1).flatten()
msg = msg[0]
print(msg)
def main():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(get_message("XXXXXXXXXXXXXXXXXXX"))
main()
Please, can anyone help? I'll be much appreciated!!!
The thing I want to do is very simple but it's so difficult for me.
The Python Discord library is completely based around events. For example, when a message happens, you will receive an event about it that you can respond to. We use async functions because most actions with the Discord API has a delay and we want to continue doing other things while we are waiting for the API response.
This makes is hard to just get the last message. We can still do it by creating a background task that waits until the API is connected, gets the message and then quits.
Based on the example here:
https://github.com/Rapptz/discord.py/blob/master/examples/background_task.py
import discord
import asyncio
class MyClient(discord.Client):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# create the background task and run it in the background
self.loop.create_task(self.get_message(1111111111111))
async def get_message(self, CHANNEL_ID):
await self.wait_until_ready()
msg = await self.get_channel(CHANNEL_ID).history(limit=1).flatten()
msg = msg[0]
print(msg)
await self.close() # close client once we are done
client = MyClient()
client.run('your token here')
I've tried setting up a WebSocket server, and an API to access it via browser
I'm a week new into Python, and I hope this somewhat makes sense.
I have a Server class
**Server.py**
# WS server that sends messages at random intervals
import asyncio
import datetime
import random
import websockets
async def time(websocket, path):
while True:
now = datetime.datetime.utcnow().isoformat() + "Z"
await websocket.send(now)
await asyncio.sleep(random.random() * 3)
async def echo(websocket, path):
async for message in websocket:
await websocket.send(message)
async def router(websocket, path):
if path == "/get":
await time(websocket)
elif path == "/post":
await echo(websocket)
asyncio.get_event_loop().run_until_complete(
websockets.serve(router, "127.0.0.1", 8081, ping_interval=None))
asyncio.get_event_loop().run_forever()
and an API class
**API.py**
from quart import Quart
import json
import websockets
app = Quart(__name__)
#app.route('/get',methods=['GET'])
async def foo1():
uri = "ws://localhost:8081"
async with websockets.connect(uri) as websocket:
try:
receivedMessage = await websocket.recv()
print(f"< {receivedMessage}")
except:
print('Reconnecting')
websocket = await websockets.connect(uri)
return ' '
#app.route('/post', methods=['POST'])
async def foo2():
uri = "ws://localhost:8081"
async with websockets.connect(uri) as websocket:
await websocket.send("Hello world!")
try:
await websocket.recv()
except:
print('Reconnecting')
websocket = await websockets.connect(uri)
if __name__ == '__main__':
app.run()
My aim was to be able to access different functions from the Server, based on the API's Path which was being accessed (seen it here: https://github.com/aaugustin/websockets/issues/547)
The problem I'm getting is that "Reconnecting" appears constantly, and nothing actually happens when I make use of /get or /post (i.e. it seems to be closing after .recv() occurs - I know this because I had seen ConnectionClosedOk - no reason 1000 earlier.
I really don't understand what I'm doing right now lol, it is very confusing. I did get things to work when Server only had 1 function (constantly spitting out timestamps), and the API only had a GET function (to display the current time of the request).. things now don't really work after I implemented the router function.
Any help would be greatly, greatly appreciated. My Python knowledge is incredibly limited, so as verbose an answer as possible would really help me out.
I'm assuming that the POST method is going to result in "Hello world!" appearing on the console for the Server, but I'm probably wrong about that :( I just seen it in some tutorial documentation.
EDIT: Also, I'm not able to access localhost:(port)/post; it says Specified method is invalid for this resource; why is that?
I am building a Discord bot in Python and would like to receive HTTP requests from Twitch.tv's API (See Webhooks Guide & Webhooks Reference) (To subscribe to events like; X streamer has gone live) and based on the content of the HTTP (POST or GET) request received from Twitch, do something on the Discord bot, e.g: Output a message on a text channel.
I am using the discord.py Python Discord API/Library.
I've looked into the matter and found that Flask seemed like a good minimalist choice for a webserver to receive these requests on.
I should preface this by saying I'm very new to Python and I've never used Flask before.
Now. The problem is I can't seem to figure out a way to run the Flask server inside of my discord bot.
I've tried adding this simple code into my discord.py script:
from flask import Flask, request
app = Flask(__name__)
#app.route('/posts', methods=['POST'])
def result():
print(request.form['sched'])
# Send a message to a discord text channel etc...
return 'Received !'
When I run my discord.py script which looks something like this:
(Stripped away some commands and features for the sake of keeping this shorter)
import discord
import asyncio
from flask import Flask, request
app = Flask(__name__)
#app.route('/posts', methods=['POST'])
def result():
print(request.form['sched'])
# Send a message to a discord text channel etc...
return 'Received !'
client = discord.Client()
#client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
#client.event
async def on_message(message):
if message.author == client.user:
return
content = message.content
fullUser = message.author.name+'#'+message.author.discriminator
print(str(message.timestamp)+" #"+message.channel.name+" "+fullUser+": "+str(content.encode('ascii', 'ignore').decode('ascii')))
if content.startswith('!'):
content = content[1:]
if content.startswith('test'):
counter = 0
tmp = await client.send_message(message.channel, 'Calculating messages...')
async for log in client.logs_from(message.channel, limit=100):
if log.author == message.author:
counter += 1
await client.edit_message(tmp, 'You have {} messages.'.format(counter))
client.run('MyTokenHere')
It seems like if I point flask to discord.py (the above) and run it, it'll start the code, get to the "client.run('MyTokenHere')" part for discord, and just stop at that and run the discord bot. It's not until I exit out of the bot by doing Ctrl+C that the actual Flask server starts, but now the discord bot is disconnected and no longer does any processing.
The same problem persists if I were to for example add "app.run()" somewhere in my code (before calling "client.run()" which starts the Discord bot part) to launch the Flask server; It'll just run the flask, get stuck on that until I Ctrl+C out of the Flask server, then it'll proceed to start the Discord bot.
Ultimately, I need to use the Discord API and I need to be connected to the Discord API gateway and all that good jazz to actually send messages to a channel with the bot, so I don't really know what to do here.
So. I think I've tried my best to explain what I'm ultimately trying to achieve here, and hopefully someone can help me find a way to either make this work with Flask, or if there's a better and easier way, provide a different solution.
This is cog example in discord.py
I made this thing for dbl (Discord Bot Lists) you can implement, the thing you need.
Note: run your heroku app with webprocess
example :
web: python main.py
Then go on https://uptimerobot.com/
and setup to ping your web app after every 5 minutes
from aiohttp import web
from discord.ext import commands, tasks
import discord
import os
import aiohttp
app = web.Application()
routes = web.RouteTableDef()
def setup(bot):
bot.add_cog(Webserver(bot))
class Webserver(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.web_server.start()
#routes.get('/')
async def welcome(request):
return web.Response(text="Hello, world")
#routes.post('/dbl')
async def dblwebhook(request):
if request.headers.get('authorization') == '3mErTJMYFt':
data = await request.json()
user = self.bot.get_user(data['user']) or await self.bot.fetch_user(data['user'])
if user is None:
return
_type = f'Tested!' if data['type'] == 'test' else f'Voted!'
upvoted_bot = f'<#{data["bot"]}>'
embed = discord.Embed(title=_type, colour=discord.Color.blurple())
embed.description = f'**Upvoter :** {user.mention} Just {_type}' + f'\n**Upvoted Bot :** {upvoted_bot}'
embed.set_thumbnail(url=user.avatar_url)
channel = self.bot.get_channel(5645646545142312312)
await channel.send(embed=embed)
return 200
self.webserver_port = os.environ.get('PORT', 5000)
app.add_routes(routes)
#tasks.loop()
async def web_server(self):
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, host='0.0.0.0', port=self.webserver_port)
await site.start()
#web_server.before_loop
async def web_server_before_loop(self):
await self.bot.wait_until_ready()
As the kind commenters informed me; threading seems like the way to go.
Thanks guys!
Another cool way if you aren't going to use flask extensions, use quart instead of flask, then it will super easy for you.
Note: Run your heroku app with webprocess
example :
web: python main.py
Then go on https://uptimerobot.com/ and setup to ping your web app after every 5 minutes
# Code Example
from discord.ext import commands
from quart import Quart
import os
app = Quart(__name__)
bot = commands.Bot('!')
#bot.command()
async def something(ctx):
...
"""
Note: On Heroku you cant bind your webserver with 5000 port as they aren't static.
To fix above problem you will have to get dynamic port from the environment variable and you are good to go.
"""
PORT = os.environ.get('PORT')
bot.loop.create_task(app.run_task('0.0.0.0', PORT))
bot.run('Token')
Or you can use the Terminal Multiplexer, tmux to run them independently!.
If you are running on a Linux platform, tmux python3 flaskapp.py would run the flask app, while you can independently run the discord bot.
You can use asyncio with flask to perform this
import discord
import asyncio
class PrintDiscordChannelsClient(discord.Client):
def __init__(self, *args, **kwargs):
self.guild_id = kwargs.pop('guild_id')
super().__init__(*args, **kwargs)
async def on_ready(self):
try:
await self.wait_until_ready()
guild = self.get_guild(int(self.guild_id))
print(f'{guild.name} is ready!')
channels = guild.text_channels
print(f'{(channels)} channels found')
await self.close()
except Exception as e:
print(e)
await self.close()
async def print_discord_channels(guild_id):
client = PrintDiscordChannelsClient(guild_id=guild_id)
await client.start('DISCORD_BOT_TOKEN')
#application.route("/discord-api", methods=["GET"])
def discord_api():
guild_id = request.args.get('guild_id')
asyncio.run(print_discord_channels(guild_id=guild_id))
return make_response("Success", 200)