Skip to content

Commit

Permalink
#103 通知系统
Browse files Browse the repository at this point in the history
  • Loading branch information
gakkiyomi committed Nov 27, 2024
1 parent 2ef52c2 commit b7eb948
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 18 deletions.
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ colorama==0.4.6
termcolor==2.4.0
prettytable==3.9.0
bs4==0.0.1
html2text==2020.1.16
html2text==2020.1.16
plyer==2.1.0
4 changes: 3 additions & 1 deletion src/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def to_config(self) -> dict:


class ChatConfig(object):
def __init__(self, blacklist: list[str] = [], kw_blacklist: list[str] = ['你点的歌来了'], repeat_mode_switch=False, frequency=5, soliloquize_switch=False,
def __init__(self, blacklist: list[str] = [], kw_notification: list[str] = [], kw_blacklist: list[str] = ['你点的歌来了'], repeat_mode_switch=False, frequency=5, soliloquize_switch=False,
soliloquize_frequency=20, sentences: list[str] = [], answer_mode: bool = False, fish_ball: str = '凌 捞鱼丸',
chat_user_color: str | None = None, chat_content_color: str | None = None):
self.repeat_mode_switch = repeat_mode_switch
Expand All @@ -67,6 +67,7 @@ def __init__(self, blacklist: list[str] = [], kw_blacklist: list[str] = ['你点
'吃饭了没有?', '💗 爱你哟!'] + sentences
self.blacklist = blacklist
self.kw_blacklist = kw_blacklist
self.kw_notification = kw_notification
self.answer_mode = answer_mode
self.fish_ball = fish_ball
self.chat_user_color = chat_user_color
Expand All @@ -83,6 +84,7 @@ def to_config(self) -> dict:
'sentences': '[' + ",".join('\"'+item+'\"' for item in self.sentences) + ']',
'blacklist': '[' + ",".join('\"'+item+'\"' for item in self.blacklist) + ']',
'kwBlacklist': '[' + ",".join('\"'+item+'\"' for item in self.kw_blacklist) + ']',
'kwNotification': '[' + ",".join('\"'+item+'\"' for item in self.kw_notification) + ']',
'chatUserColor': self.chat_user_color,
'chatContentColor': self.chat_content_color
}
Expand Down
5 changes: 5 additions & 0 deletions src/api/enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
import enum
NTYPE = enum.Enum('Notification_type', [
'FROM_CHATROOM', 'FROM_CHAT', 'FROM_KEYWORD'])
CODE = enum.Enum('REDPACKET_CODE', ['SUCCESS', 'LOSED', 'NOT_ME', "ZERO"])
3 changes: 3 additions & 0 deletions src/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ def init_chat_config(config: ConfigParser) -> ChatConfig:
ret.kw_blacklist = json.loads(config.get('chat', 'kwBlacklist'))
if ret.kw_blacklist.__contains__(''):
ret.kw_blacklist.remove('')
ret.kw_notification = json.loads(config.get('chat', 'kwNotification'))
if ret.kw_notification.__contains__(''):
ret.kw_notification.remove('')
ret.fish_ball = config.get('chat', "fishBall")
init_chat_color(ret, config)
return ret
Expand Down
36 changes: 30 additions & 6 deletions src/core/chatroom.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
# -*- coding: utf-8 -*-
import random
import re
import schedule
from concurrent.futures import ThreadPoolExecutor
from urllib.parse import parse_qs, urlparse

import schedule
from prettytable import PrettyTable
from termcolor import colored

from src.api import API, FishPi
from src.api.config import GLOBAL_CONFIG
from src.api.enum import NTYPE
from src.api.ws import WS

from .notification import Event, sender, sys_notification
from .redpacket import render_redpacket, rush_redpacket

REPEAT_POOL = {} # 复读池
Expand Down Expand Up @@ -50,6 +52,8 @@ def render(api: FishPi, message: dict) -> None:
executor.submit(rush_redpacket, api, message)
else:
renderChatroomMsg(api, message)
at_notification(api, message)
kw_notification(api, message)


def renderChatroomMsg(api: FishPi, message: dict) -> None:
Expand All @@ -65,11 +69,8 @@ def renderChatroomMsg(api: FishPi, message: dict) -> None:
f'\t\t\t\t\t\t你说: {message["md"]}', GLOBAL_CONFIG.chat_config.chat_user_color))
api.chatroom.last_msg_id = message['oId']
else:
if len(GLOBAL_CONFIG.chat_config.kw_blacklist) > 0:
hasKeyword = any(
i for i in GLOBAL_CONFIG.chat_config.kw_blacklist if message["md"].__contains__(i))
if hasKeyword:
return
if _kw_blacklist(api, message):
return
if "client" in message:
print(f'[{time}] 来自({message["client"]})')
else:
Expand Down Expand Up @@ -150,3 +151,26 @@ def renderWeather(username: str, lines: list[str]) -> str:
table.add_row(row_data)
lines[index] = table.get_string()
return '\n'.join(lines)


def at_notification(api: FishPi, message: dict) -> None:
if message["userName"] != api.current_user and message["md"].__contains__(f'@{api.current_user}'):
sender(Event(type=NTYPE.FROM_CHATROOM, sender=message["userName"],
content=message['md']), sys_notification)


def kw_notification(api: FishPi, message: dict) -> None:
if len(GLOBAL_CONFIG.chat_config.kw_notification) == 0:
return
if message["userName"] != api.current_user and any(
i for i in GLOBAL_CONFIG.chat_config.kw_notification if message["md"].__contains__(i)):
sender(Event(type=NTYPE.FROM_KEYWORD, sender=message["userName"],
content=message['md']), sys_notification)


def _kw_blacklist(api: FishPi, message: dict) -> bool:
if len(GLOBAL_CONFIG.chat_config.kw_blacklist) > 0:
return any(
i for i in GLOBAL_CONFIG.chat_config.kw_blacklist if message["md"].__contains__(i))
else:
return False
21 changes: 19 additions & 2 deletions src/core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
release_someone,
remove_keyword_to_bl,
)
from .notification import (
put_keyword_to_nitification,
remove_keyword_to_nitification
)
from .chat import Chat
from .chatroom import ChatRoom
from .user import User, render_online_users, render_user_info
Expand Down Expand Up @@ -265,7 +269,7 @@ def exec(self, api: FishPi, args: Tuple[str, ...]):
elif ban_type == 'user':
ban_someone(api, ' '.join(it))
else:
print('非法指令, ban指令应该为: ban keyword|user name')
print('非法指令, ban指令应该为: ban (keyword|user) 用户名')


class ReleaseSomeoneCommand(Command):
Expand All @@ -277,7 +281,19 @@ def exec(self, api: FishPi, args: Tuple[str, ...]):
elif release_type == 'user':
release_someone(api, ' '.join(it))
else:
print('非法指令, release指令应该为: release keyword|user name')
print('非法指令, release指令应该为: release (keyword|user) 用户名')


class NotificationKeywordCommand(Command):
def exec(self, api: FishPi, args: Tuple[str, ...]):
it = (i for i in args)
type = next(it)
if type == '-d':
remove_keyword_to_nitification(it)
elif type == '-a':
put_keyword_to_nitification(it)
else:
print('非法指令, notification指令应该为: notification (-d | -a)) keyword')


class GetUserInfoCommand(Command):
Expand Down Expand Up @@ -476,6 +492,7 @@ def init_cli(api: FishPi):
cli_handler.add_command('#blacklist', BlackListCommand())
cli_handler.add_command('#ban', BanSomeoneCommand())
cli_handler.add_command('#release', ReleaseSomeoneCommand())
cli_handler.add_command('#notification', NotificationKeywordCommand())
cli_handler.add_command('#rp', RedpacketCommand())
cli_handler.add_command('#rp-ave', AVGRedpacketCommand())
cli_handler.add_command('#rp-hb', HBRedpacketCommand())
Expand Down
109 changes: 109 additions & 0 deletions src/core/notification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
from abc import ABC
import queue
import re
from plyer import notification

from src.api.config import GLOBAL_CONFIG
from src.api.enum import NTYPE
from src.utils.file import project_root


class Event(ABC):

def __init__(self, type: NTYPE, sender: str, content: str):
self.type = type
self.sender = sender
self.content = content

def __str__(self):
return f'{self.type} {self.sender}: {self.content}'


EVENT_QUEUE = queue.Queue[Event]()


def sender(event: Event, *consumers):
for consumer in consumers:
consumer(event)
EVENT_QUEUE.put(event)


def flush():
events: list[Event] = []
while not EVENT_QUEUE.empty():
event = EVENT_QUEUE.get()
events.append(event)
EVENT_QUEUE.task_done()

if events:
# 批量写入文件
with open("./events.log", "a") as f:
for event in events:
f.write(f"{str(event)}\n")


def sys_notification(event: Event):
notification.notify(
title=render_func[event.type](event.sender),
app_name='摸鱼派python客户端',
app_icon=f'{project_root}/icon.ico',
message=f'{event.sender}: {event.content}',
timeout=5 # 通知持续时间(秒)
)


render_func = {
NTYPE.FROM_CHATROOM: lambda user: f'{user}在聊天室@你',
NTYPE.FROM_CHAT: lambda user: f'{user}发送了一条私聊信息',
NTYPE.FROM_KEYWORD: lambda _: '关心的消息',
}


def put_keyword_to_nitification(args: tuple[str, ...]) -> None:
for keyword in args:
if GLOBAL_CONFIG.chat_config.kw_notification.__contains__(keyword):
print(f'{keyword} 已在加入关键词提醒')
continue
GLOBAL_CONFIG.chat_config.kw_notification.append(keyword)
print(f'{keyword} 已在加入关键词提醒')
if GLOBAL_CONFIG.cfg_path is None:
return
# 持久化到文件
lines: list[str] = []
with open(GLOBAL_CONFIG.cfg_path, "r+", encoding='utf-8') as src:
lines = src.readlines()

for i in range(len(lines)):
lines[i] = re.sub(r'^kw[nN]tification\s*=.*', "kwNtification=" +
str(GLOBAL_CONFIG.chat_config.kw_notification).replace("\'", "\""), lines[i])
with open(GLOBAL_CONFIG.cfg_path, 'w', encoding='utf-8') as dst:
dst.write("".join(lines))


def remove_keyword_to_nitification(args: tuple[str, ...]) -> None:
for keyword in args:
if GLOBAL_CONFIG.chat_config.kw_notification.__contains__(keyword) == False:
print(f'{keyword} 不在关键词提醒池中')
continue
GLOBAL_CONFIG.chat_config.kw_notification.remove(keyword)
print(f'{keyword} 不再提醒')
if GLOBAL_CONFIG.cfg_path is None:
return
# 持久化到文件
lines: list[str] = []

after: str = ''
if len(GLOBAL_CONFIG.chat_config.kw_notification) == 0:
after = 'kwNtification=[]'
else:
after = "kwNtification=" + \
str(GLOBAL_CONFIG.chat_config.kw_notification).replace("\'", "\"")

with open(GLOBAL_CONFIG.cfg_path, "r+", encoding='utf-8') as src:
lines = src.readlines()

for i in range(len(lines)):
lines[i] = re.sub(r'^kw[nN]tification\s*=.*', after, lines[i])
with open(GLOBAL_CONFIG.cfg_path, 'w', encoding='utf-8') as dst:
dst.write("".join(lines))
3 changes: 1 addition & 2 deletions src/core/redpacket.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import json
import time

from src.api.enum import CODE
from src.api import FishPi
from src.api.config import GLOBAL_CONFIG
from src.utils import RPS_LOSED, RPS_SUCCESS, RPS_ZERO

CODE = enum.Enum('REDPACKET_CODE', ['SUCCESS', 'LOSED', 'NOT_ME', "ZERO"])


def __open_redpacket_render(username, redpacket: dict) -> CODE:
who = redpacket['who']
Expand Down
12 changes: 11 additions & 1 deletion src/core/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

from src.api import FishPi, UserInfo
from src.api.ws import WS
from src.api.enum import NTYPE
from .notification import (sender, Event, sys_notification)


class User(WS):
WS_URL = 'fishpi.cn/user-channel'

def __init__(self) -> None:
super().__init__(User.WS_URL, [])
super().__init__(User.WS_URL, [chat_notification])

def on_open(self, ws):
pass
Expand All @@ -24,6 +26,14 @@ def online(self, user: UserInfo):
self.start()


def chat_notification(api: FishPi, message: dict) -> None:
print('收到私信')
if 'newIdleChatMessage' != message['command']:
return
sender(Event(type=NTYPE.FROM_CHAT, sender=message["senderUserName"],
content=message['preview']), sys_notification)


def render_user_info(userInfo):
print("用户ID: " + userInfo['oId'])
print("用户名: " + userInfo['userName'])
Expand Down
11 changes: 6 additions & 5 deletions src/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
[#chatroom] 进入聊天室模式
[#chat] 私聊 #chat Gakkiyomi 进入和Gakkiyomi的私聊窗口
[#siguo] 思过崖
[#article] 看帖 (默认显示20个帖子) [view|page] {int} / 回帖 #article comment {str}
[#article] 看帖 (默认显示20个帖子) [view|page] (int) / 回帖 #article comment (str)
[#rp] 1 128 1个128积分 (默认5个,128积分)拼手气红包
[#rp-ave] 1 128 1个128积分 (默认5个,32积分)平均红包
[#rp-hb] 5 128 5个128积分 (默认5个,32积分)心跳红包
Expand All @@ -37,6 +37,7 @@
[#blacklist] 查看黑名单列表
[#ban keyword|user xxx] 将某人或者关键词送入黑名单
[#release keyword|user xxx] 将某人或者关键词解除黑名单
[#notification {-d|-a}}] keyword 动态修改关键词提醒
[#liveness] 查看当前活跃度(⚠️慎用,如果频繁请求此命令(最少间隔30s),登录状态会被直接注销,需要重启脚本!)
'''

Expand All @@ -45,10 +46,10 @@
RPS_ZERO = '![](https://file.fishpi.cn/2023/05/1683183148506-4c31497e.png)'


RP_RE = re.compile('(\d) (\d+)')
RP_SEND_TO_CODE_RE = re.compile('(\d+) ([\w,]+)(?<!,)$')
RP_TIME_CODE_RE = re.compile('(\d+)')
TRANSFER_RE = re.compile('(\d+) (\w+)( \S+)?')
RP_RE = re.compile(r'(\d) (\d+)')
RP_SEND_TO_CODE_RE = re.compile(r'(\d+) ([\w,]+)(?<!,)$')
RP_TIME_CODE_RE = re.compile(r'(\d+)')
TRANSFER_RE = re.compile(r'(\d+) (\w+)( \S+)?')


def cli_login(cli_user: str) -> bool:
Expand Down
5 changes: 5 additions & 0 deletions src/utils/file.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# -*- coding: utf-8 -*-
import os
from pathlib import Path


def ensure_directory_exists(file_path):
directory = os.path.dirname(file_path)

if not os.path.exists(directory):
os.makedirs(directory)


# 项目根目录
project_root = Path(__file__).parent.parent.parent.resolve()

0 comments on commit b7eb948

Please sign in to comment.