i created a simple websocket server using aiohttp . my server reads message from redis pub/sub and sends it to client .
this is my websocket code:
import aiohttp
from aiohttp import web
import aioredis
router = web.RouteTableDef()
#router.get("/ws")
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
sub = request.config_dict["REDIS"]
ch, *_ = await sub.subscribe('hi')
async for msg in ch.iter(encoding='utf-8'):
await ws.send_str('{}: {}'.format(ch.name, msg))
async def init_redis(app):
redis_pool = await aioredis.create_redis_pool('redis://localhost')
app["REDIS"] = redis_pool
yield
redis_pool.close()
await redis_pool.wait_closed()
async def init_app():
app = web.Application()
app.add_routes(router)
app.cleanup_ctx.append(init_redis)
return app
web.run_app(init_app())
my first client can connect to server and receive messages but when i create another client to connect to this endpoint it receive no messages !
what is the problem ? how can i fix this problem?
You need to call create_redis for each client and publish the message to the channel. Otherwise, only the first client will receive the subscribed message.
So, you can edit your code as follows.
import aiohttp
from aiohttp import web
import aioredis
import asyncio
router = web.RouteTableDef()
async def reader(ws, ch):
while (await ch.wait_message()):
await ws.send_str('{}: {}'.format(ch.name, msg))
#router.get("/ws")
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
sub = await aioredis.create_redis_pool('redis://localhost')
pub = await aioredis.create_redis_pool('redis://localhost')
ch, *_ = await sub.subscribe('hi')
asyncio.ensure_future(reader(ws, ch))
async for msg in ws:
await pub.publish('hi', msg)
sub.close()
pub.close()
async def init_app():
app = web.Application()
app.add_routes(router)
return app
web.run_app(init_app())
Please note there may be minor syntax error (e.g. format of the message), but this is the structure that you should follow as it worked for me. I got it to work with my application.
Related
Basically I was doing a small bot for telegram to send pictures from a subreddit, but it gives me an error which I don't know how to fix. Everything where it says (not shown) is something I can't show due to it being something with which anyone could use it under something I created, but I guess you don't really need it.
client_session: <aiohttp.client.ClientSession object at 0x000001555709DD90>```
config.py:
```settings = {
"CLIENT_ID": "(not shown)",
"SECRET_CODE":"(not shown)",
"TOKEN":"(not shown)"
} ```
telegram_bot.py:
```import asyncio
import aiohttp
import config
import asyncpraw
from aiogram import Bot, types
API_TOKEN = config.settings["TOKEN"]
CHANNEL_ID = -1001374273592
bot = Bot(token=API_TOKEN, parse_mode=types.ParseMode.HTML)
reddit = asyncpraw.Reddit(client_id=config.settings["CLIENT_ID"],
client_secret=config.settings["SECRET_CODE"],
user_agent="random_raddit_bot/0.0.1")
mems = []
TIMEOUT = 5
SUBREDDIT_NAME = "memes"
POST_LIMIT = 1
async def send_message(channel_id: int, txt: str):
await bot.send_message(channel_id, text)
async def main():
while True:
await asyncio.sleep(TIMEOUT)
memes_submissions = await reddit.subreddit(SUBREDDIT_NAME)
memes_submissions = memes_submissions.new(limit=POST_LIMIT)
item = await memes_submissions.__anext__()
if item.titles not in mems:
mems.append(item.title)
await send_message(CHANNEL_ID, item.url)
asyncio.get_event_loop().run_until_complete```
I'm trying to understand how to use an async function inside another function:
I'm trying to send messages via Telegram whenever something happens during my code.
Telegram logger:
from telethon import TelegramClient, sync
import asyncio
async def msgtelegram(entity, msg,api_id=xxx, api_hash='xx'):
client = TelegramClient('Session', api_id, api_hash)
await client.start()
if not await client.is_user_authorized():
await client.sign_in('xxx')
await client.send_message(entity, msg)
await client.disconnect()
def func():
try:
print('hello!')
except:
await msgtelegram(entity, msg)
Calling await msgtelegram() outside of a function seems to work correctly.
Am I missing something or am I using async wrong?
Thank you!
you could use either asyncio.run() or loop.run_until_complete() so for your example it would be
from telethon import TelegramClient, sync
import asyncio
async def msgtelegram(entity, msg,api_id=xxx, api_hash='xx'):
client = TelegramClient('Session', api_id, api_hash)
await client.start()
if not await client.is_user_authorized():
await client.sign_in('xxx')
await client.send_message(entity, msg)
await client.disconnect()
def func():
try:
print('hello!')
except:
asyncio.run(msgtelegram(entity, msg))
I'm a complete beginner when it comes to socket, so please bear with me if the question seems too trivial for you.
The following is a code that i found on GitLab and I'm trying to understand
import os
import logging
import uuid
import socketio
from aiohttp import web
import sys
sys.path.append('.')
logging.basicConfig(level=logging.WARN,
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
datefmt='%m-%d %H:%M')
# Home page
async def index(request):
index_file = open('examples/rasa_demo/templates/index.html')
return web.Response(body=index_file.read().encode('utf-8'), headers={'content-type': 'text/html'})
# Action endpoint
async def webhook(request):
"""Webhook to retrieve action calls."""
action_call = await request.json()
try:
response = await executor.run(action_call)
except ActionExecutionRejection as e:
logger.error(str(e))
response = {"error": str(e), "action_name": e.action_name}
response.status_code = 400
return response
return web.json_response(response)
# Web app routing
app = web.Application()
app.add_routes([
web.get('/', index),
web.post('/webhook', webhook),
web.static('/static', 'examples/rasa_demo/static')
])
# Instantiate all bot agents
bots = BotFactory.createAll()
# Websocket through SocketIO with support for regular HTTP endpoints
sio = socketio.AsyncServer(async_mode='aiohttp', cors_allowed_origins='*')
sio.attach(app)
#sio.on('session_request')
async def on_session_request(sid, data):
if data is None:
data = {}
if 'session_id' not in data or data['session_id'] is None:
data['session_id'] = uuid.uuid4().hex
await sio.emit('session_confirm', data['session_id'])
#sio.on('user_uttered')
async def on_user_uttered(sid, message):
custom_data = message.get('customData', {})
lang = custom_data.get('lang', 'en')
user_message = message.get('message', '')
bot_responses = await bots[lang].handle_text(user_message) #await BotFactory.getOrCreate(lang).handle_text(user_message)
for bot_response in bot_responses:
json = __parse_bot_response(bot_response)
await sio.emit('bot_uttered', json, room=sid)
What I'm trying to understand is how do the event handlers catch or events like 'session_request' or'user_uttered' when they were never emitted.
Thank you.
This program on Jupyter notebook connect only with first websocket. What is wrong?
session1 = aiohttp.ClientSession()
session2 = aiohttp.ClientSession()
async with session1.ws_connect('host1') as ws1:
async for msg1 in ws1:
print(msg1.data)
await asyncio.sleep(5)
async with session2.ws_connect('host2') as ws2:
async for msg2 in ws2:
print(msg2.data)
await asyncio.sleep(5)
import asyncio
import aiohttp
urls = [host1, host2, ...]
async def websocket(url):
session = aiohttp.ClientSession()
async with session.ws_connect(url) as ws:
async for msg in ws:
print(msg.data)
loop = asyncio.get_event_loop()
tasks = [websocket(url) for url in urls]
loop.run_until_complete(asyncio.wait(tasks))
EDIT: correction after #Ken4scholars comment below
I have the following consumer which fails right after connecting
consumers.py
from channels.generic.websocket import AsyncJsonWebsocketConsumer
#...
class ListGeneratedTokensByFileConsumer(AsyncJsonWebsocketConsumer):
stop = False
async def websocket_connect(self,event):
await self.accept()
self.stop = False
async def websocket_receive(self,event):
await self.send_json({"text":"received","accept": True})
await self.send_tokens_list()
async def websocket_disconnect(self,event):
self.stop = True
async def send_tokens_list(self):
some_path = "..."
while self.stop == False:
await asyncio.sleep(2)
the_message = {}
if os.path.isfile("some_file.json")):
with open(os.path.join(some_path ,"some_file.json"),'r') as new_tok:
the_message = json.load(new_tok)
if not the_message:
print("waiting...")
else:
await self.send_json(the_message)
await self.close()
It always throws the error: ERR_CONNECTION:RESEST and the websocket disconnects with code 1006. This might seem familiar to recent changes in django-channels but since I am sending a text once the websocket opens and send a message back from the consumer it should do the trick. Or is there something wrong?
routing.py
url(r'^myapp/sub_path/(?P<pk>\d+)/sub_sub_path/',ListGeneratedTokensByFileConsumer)
and the websocket endpoint in js is:
.js
var loc = window.location;
var wsStart = "ws://";
if (loc.protocol == "https:") {
wsStart = "wss://";
}
var endpoint = wsStart + loc.host + loc.pathname + "sub_sub_path" + "/";
for info, with channels-redis==2.3.2, channels==2.3.0, asgiref==3.2.2, daphne==2.3.0, django==2.0.8
If you see something like in django logs:
WebSocket HANDSHAKING /ws/your_route
WebSocket REJECT /ws/your_route
WebSocket DISCONNECT /ws/your_route
And you wrapped websocket router with AllowedHostsOriginValidator in the asgi.py like:
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AllowedHostsOriginValidator(
URLRouter(
chat_websocket_urlpatterns
))
})
Then you definetely should check the ALLOWED_HOSTS variable in the settings.py, maybe you forgot to put something in there. In my case it was just because I didn't specify my IP address, only localhost.
This is most likely not that the asker looked for, but I thought it may come handy to someone else, as there is not much info about the django channels in this site.