chat_room.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. """
  2. 聊天室
  3. ^^^^^^^^^^^
  4. 和当前所有在线的人聊天
  5. :demo_host:`Demo地址 </?pywebio_api=chat_room>` `源码 <https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py>`_
  6. * 使用基于协程的会话
  7. * 使用 `run_async() <pywebio.session.run_async>` 启动后台协程
  8. """
  9. import asyncio
  10. from pywebio import start_server, run_async
  11. from pywebio.input import *
  12. from pywebio.output import *
  13. from pywebio.session import defer_call, set_env, run_js
  14. # 最大消息记录保存
  15. MAX_MESSAGES_CNT = 10 ** 4
  16. chat_msgs = [] # 聊天记录 (name, msg)
  17. online_users = set() # 在线用户
  18. async def refresh_msg(my_name, msg_box):
  19. """刷新聊天消息"""
  20. global chat_msgs
  21. last_idx = len(chat_msgs)
  22. while True:
  23. await asyncio.sleep(0.5)
  24. for m in chat_msgs[last_idx:]:
  25. if m[0] != my_name: # 仅刷新其他人的新信息
  26. msg_box.append(put_markdown('`%s`: %s' % m))
  27. # 清理聊天记录
  28. if len(chat_msgs) > MAX_MESSAGES_CNT:
  29. chat_msgs = chat_msgs[len(chat_msgs) // 2:]
  30. last_idx = len(chat_msgs)
  31. async def main():
  32. """PyWebIO聊天室
  33. 和当前所有在线的人聊天
  34. """
  35. global chat_msgs
  36. put_markdown("## PyWebIO聊天室\n欢迎来到聊天室,你可以和当前所有在线的人聊天。你可以在浏览器的多个标签页中打开本页面来测试聊天效果。"
  37. "本应用使用不到80行代码实现,源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)", lstrip=True)
  38. msg_box = output()
  39. put_scrollable(msg_box, height=300, keep_bottom=True)
  40. nickname = await input("请输入你的昵称", required=True, validate=lambda n: '昵称已被使用' if n in online_users or n == '📢' else None)
  41. online_users.add(nickname)
  42. chat_msgs.append(('📢', '`%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users))))
  43. msg_box.append(put_markdown('`📢`: `%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users))))
  44. @defer_call
  45. def on_close():
  46. online_users.remove(nickname)
  47. chat_msgs.append(('📢', '`%s`退出聊天室. 当前在线人数 %s' % (nickname, len(online_users))))
  48. refresh_task = run_async(refresh_msg(nickname, msg_box))
  49. while True:
  50. data = await input_group('发送消息', [
  51. input(name='msg', help_text='消息内容支持行内Markdown语法'),
  52. actions(name='cmd', buttons=['发送', '多行输入', {'label': '退出', 'type': 'cancel'}])
  53. ], validate=lambda d: ('msg', '消息不为空') if d['cmd'] == '发送' and not d['msg'] else None)
  54. if data is None:
  55. break
  56. if data['cmd'] == '多行输入':
  57. data['msg'] = '\n' + await textarea('消息内容', help_text='消息内容支持Markdown语法')
  58. msg_box.append(put_markdown('`%s`: %s' % (nickname, data['msg']), sanitize=True))
  59. chat_msgs.append((nickname, data['msg']))
  60. refresh_task.close()
  61. toast("你已经退出聊天室")
  62. if __name__ == '__main__':
  63. start_server(main, debug=True, port=8080, cdn=False)