Compare commits

..

16 Commits

Author SHA1 Message Date
jxxghp
f6981734d0 更新 version.py 2025-01-28 16:06:03 +08:00
jxxghp
cb6aa61b6b fix apis 2025-01-27 17:56:32 +08:00
jxxghp
2ed9cfcc9a fix api 2025-01-27 17:08:22 +08:00
jxxghp
2e796f41cb fix api 2025-01-27 13:45:57 +08:00
jxxghp
7d13e43c6f fix apis 2025-01-27 11:09:05 +08:00
jxxghp
db684de6e9 Merge pull request #3815 from Akimio521/fix/transfer-background 2025-01-27 08:16:22 +08:00
Akimio521
510ef59aa0 fix: 计算任务时某些fileitem.size是None 2025-01-27 00:35:41 +08:00
jxxghp
d56083a29e Merge pull request #3810 from Akimio521/feat/alist-token 2025-01-26 08:54:14 +08:00
Akimio521
8aed2b334e feat: 支持使用永久令牌进行认证 2025-01-25 22:31:53 +08:00
jxxghp
3bf27f224c Merge pull request #3808 from InfinityPacer/feature/plugin 2025-01-25 07:42:44 +08:00
InfinityPacer
dc9a54e74f fix(command): ensure command data isolation by using deepcopy 2025-01-25 00:32:42 +08:00
InfinityPacer
79dc194dd6 feat(plugin): add kwargs support for post_message 2025-01-25 00:18:08 +08:00
jxxghp
8e12249201 Merge pull request #3804 from InfinityPacer/feature/subscribe 2025-01-24 17:46:00 +08:00
InfinityPacer
4fa8f5b248 feat(event): use latest subscribe_info in SubscribeModified 2025-01-24 17:26:54 +08:00
InfinityPacer
3089c0c524 feat(event): add old_subscribe_info to event and update triggers 2025-01-24 17:24:29 +08:00
jxxghp
ba1ca0819e fix 关注订阅时判断历史记录 2025-01-23 13:16:32 +08:00
18 changed files with 247 additions and 49 deletions

View File

@@ -21,6 +21,21 @@ def calendar(page: int = 1,
return RecommendChain().bangumi_calendar(page=page, count=count)
@router.get("/subjects", summary="搜索Bangumi", response_model=List[schemas.MediaInfo])
def bangumi_subjects(type: int = 2,
cat: int = None,
sort: str = 'rank',
year: int = None,
page: int = 1,
count: int = 30,
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
"""
搜索Bangumi
"""
return RecommendChain().bangumi_discover(type=type, cat=cat, sort=sort, year=year,
page=page, count=count)
@router.get("/credits/{bangumiid}", summary="查询Bangumi演职员表", response_model=List[schemas.MediaPerson])
def bangumi_credits(bangumiid: int,
page: int = 1,
@@ -61,13 +76,14 @@ def bangumi_person(person_id: int,
@router.get("/person/credits/{person_id}", summary="人物参演作品", response_model=List[schemas.MediaInfo])
def bangumi_person_credits(person_id: int,
page: int = 1,
count: int = 20,
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
"""
根据人物ID查询人物参演作品
"""
medias = BangumiChain().person_credits(person_id=person_id)
if medias:
return [media.to_dict() for media in medias[(page - 1) * 20: page * 20]]
return [media.to_dict() for media in medias[(page - 1) * count: page * count]]
return []

View File

@@ -109,6 +109,7 @@ def update_subscribe(
if not subscribe:
return schemas.Response(success=False, message="订阅不存在")
# 避免更新缺失集数
old_subscribe_dict = subscribe.to_dict()
subscribe_dict = subscribe_in.dict()
if not subscribe_in.lack_episode:
# 没有缺失集数时缺失集数清空避免更新为0
@@ -126,7 +127,8 @@ def update_subscribe(
# 发送订阅调整事件
eventmanager.send_event(EventType.SubscribeModified, {
"subscribe_id": subscribe.id,
"subscribe_info": subscribe_dict,
"old_subscribe_info": old_subscribe_dict,
"subscribe_info": subscribe.to_dict(),
})
return schemas.Response(success=True)
@@ -146,9 +148,16 @@ def update_subscribe_status(
valid_states = ["R", "P", "S"]
if state not in valid_states:
return schemas.Response(success=False, message="无效的订阅状态")
old_subscribe_dict = subscribe.to_dict()
subscribe.update(db, {
"state": state
})
# 发送订阅调整事件
eventmanager.send_event(EventType.SubscribeModified, {
"subscribe_id": subscribe.id,
"old_subscribe_info": old_subscribe_dict,
"subscribe_info": subscribe.to_dict(),
})
return schemas.Response(success=True)
@@ -213,11 +222,18 @@ def reset_subscribes(
"""
subscribe = Subscribe.get(db, subid)
if subscribe:
old_subscribe_dict = subscribe.to_dict()
subscribe.update(db, {
"note": [],
"lack_episode": subscribe.total_episode,
"state": "R"
})
# 发送订阅调整事件
eventmanager.send_event(EventType.SubscribeModified, {
"subscribe_id": subscribe.id,
"old_subscribe_info": old_subscribe_dict,
"subscribe_info": subscribe.to_dict(),
})
return schemas.Response(success=True)
return schemas.Response(success=False, message="订阅不存在")

View File

@@ -118,6 +118,11 @@ def tmdb_person_credits(person_id: int,
def tmdb_movies(sort_by: str = "popularity.desc",
with_genres: str = "",
with_original_language: str = "",
with_keywords: str = "",
with_watch_providers: str = "",
vote_average: float = 0,
vote_count: int = 0,
release_date: str = "",
page: int = 1,
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
"""
@@ -126,6 +131,11 @@ def tmdb_movies(sort_by: str = "popularity.desc",
return RecommendChain().tmdb_movies(sort_by=sort_by,
with_genres=with_genres,
with_original_language=with_original_language,
with_keywords=with_keywords,
with_watch_providers=with_watch_providers,
vote_average=vote_average,
vote_count=vote_count,
release_date=release_date,
page=page)
@@ -133,6 +143,11 @@ def tmdb_movies(sort_by: str = "popularity.desc",
def tmdb_tvs(sort_by: str = "popularity.desc",
with_genres: str = "",
with_original_language: str = "",
with_keywords: str = "",
with_watch_providers: str = "",
vote_average: float = 0,
vote_count: int = 0,
release_date: str = "",
page: int = 1,
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
"""
@@ -141,6 +156,11 @@ def tmdb_tvs(sort_by: str = "popularity.desc",
return RecommendChain().tmdb_tvs(sort_by=sort_by,
with_genres=with_genres,
with_original_language=with_original_language,
with_keywords=with_keywords,
with_watch_providers=with_watch_providers,
vote_average=vote_average,
vote_count=vote_count,
release_date=release_date,
page=page)

View File

@@ -17,6 +17,12 @@ class BangumiChain(ChainBase, metaclass=Singleton):
"""
return self.run_module("bangumi_calendar")
def discover(self, **kwargs) -> Optional[List[MediaInfo]]:
"""
发现Bangumi番剧
"""
return self.run_module("bangumi_discover", **kwargs)
def bangumi_info(self, bangumiid: int) -> Optional[dict]:
"""
获取Bangumi信息

View File

@@ -157,8 +157,15 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def tmdb_movies(self, sort_by: str = "popularity.desc", with_genres: str = "",
with_original_language: str = "", page: int = 1) -> Any:
def tmdb_movies(self, sort_by: str = "popularity.desc",
with_genres: str = "",
with_original_language: str = "",
with_keywords: str = "",
with_watch_providers: str = "",
vote_average: float = 0,
vote_count: int = 0,
release_date: str = "",
page: int = 1) -> List[dict]:
"""
TMDB热门电影
"""
@@ -166,13 +173,25 @@ class RecommendChain(ChainBase, metaclass=Singleton):
sort_by=sort_by,
with_genres=with_genres,
with_original_language=with_original_language,
with_keywords=with_keywords,
with_watch_providers=with_watch_providers,
vote_average=vote_average,
vote_count=vote_count,
release_date=release_date,
page=page)
return [movie.to_dict() for movie in movies] if movies else []
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def tmdb_tvs(self, sort_by: str = "popularity.desc", with_genres: str = "",
with_original_language: str = "zh|en|ja|ko", page: int = 1) -> Any:
def tmdb_tvs(self, sort_by: str = "popularity.desc",
with_genres: str = "",
with_original_language: str = "zh|en|ja|ko",
with_keywords: str = "",
with_watch_providers: str = "",
vote_average: float = 0,
vote_count: int = 0,
release_date: str = "",
page: int = 1) -> List[dict]:
"""
TMDB热门电视剧
"""
@@ -180,12 +199,17 @@ class RecommendChain(ChainBase, metaclass=Singleton):
sort_by=sort_by,
with_genres=with_genres,
with_original_language=with_original_language,
with_keywords=with_keywords,
with_watch_providers=with_watch_providers,
vote_average=vote_average,
vote_count=vote_count,
release_date=release_date,
page=page)
return [tv.to_dict() for tv in tvs] if tvs else []
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def tmdb_trending(self, page: int = 1) -> Any:
def tmdb_trending(self, page: int = 1) -> List[dict]:
"""
TMDB流行趋势
"""
@@ -194,7 +218,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def bangumi_calendar(self, page: int = 1, count: int = 30) -> Any:
def bangumi_calendar(self, page: int = 1, count: int = 30) -> List[dict]:
"""
Bangumi每日放送
"""
@@ -203,7 +227,24 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_movie_showing(self, page: int = 1, count: int = 30) -> Any:
def bangumi_discover(self, type: int = 2,
cat: int = None,
sort: str = 'rank',
year: int = None,
count: int = 30,
page: int = 1) -> List[dict]:
"""
搜索Bangumi
"""
medias = self.bangumichain.discover(type=type, cat=cat, sort=sort, year=year,
limit=count, offset=(page - 1) * count)
if medias:
return [media.to_dict() for media in medias]
return []
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_movie_showing(self, page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣正在热映
"""
@@ -212,7 +253,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_movies(self, sort: str = "R", tags: str = "", page: int = 1, count: int = 30) -> Any:
def douban_movies(self, sort: str = "R", tags: str = "", page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣最新电影
"""
@@ -222,7 +263,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_tvs(self, sort: str = "R", tags: str = "", page: int = 1, count: int = 30) -> Any:
def douban_tvs(self, sort: str = "R", tags: str = "", page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣最新电视剧
"""
@@ -232,7 +273,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_movie_top250(self, page: int = 1, count: int = 30) -> Any:
def douban_movie_top250(self, page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣电影TOP250
"""
@@ -241,7 +282,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_tv_weekly_chinese(self, page: int = 1, count: int = 30) -> Any:
def douban_tv_weekly_chinese(self, page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣国产剧集榜
"""
@@ -250,7 +291,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_tv_weekly_global(self, page: int = 1, count: int = 30) -> Any:
def douban_tv_weekly_global(self, page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣全球剧集榜
"""
@@ -259,7 +300,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_tv_animation(self, page: int = 1, count: int = 30) -> Any:
def douban_tv_animation(self, page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣热门动漫
"""
@@ -268,7 +309,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_movie_hot(self, page: int = 1, count: int = 30) -> Any:
def douban_movie_hot(self, page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣热门电影
"""
@@ -277,7 +318,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
@log_execution_time(logger=logger)
@cached(ttl=recommend_ttl, region=recommend_cache_region)
def douban_tv_hot(self, page: int = 1, count: int = 30) -> Any:
def douban_tv_hot(self, page: int = 1, count: int = 30) -> List[dict]:
"""
豆瓣热门电视剧
"""

View File

@@ -798,6 +798,11 @@ class SubscribeChain(ChainBase, metaclass=Singleton):
doubanid=share_sub.get("doubanid"),
season=share_sub.get("season")):
continue
# 已经订阅过跳过
if self.subscribeoper.exist_history(tmdbid=share_sub.get("tmdbid"),
doubanid=share_sub.get("doubanid"),
season=share_sub.get("season")):
continue
# 去除无效属性
for key in list(share_sub.keys()):
if not hasattr(schemas.Subscribe(), key):

View File

@@ -14,19 +14,38 @@ class TmdbChain(ChainBase, metaclass=Singleton):
TheMovieDB处理链单例运行
"""
def tmdb_discover(self, mtype: MediaType, sort_by: str, with_genres: str,
with_original_language: str, page: int = 1) -> Optional[List[MediaInfo]]:
def tmdb_discover(self, mtype: MediaType,
sort_by: str,
with_genres: str,
with_original_language: str,
with_keywords: str,
with_watch_providers: str,
vote_average: float,
vote_count: int,
release_date: str,
page: int = 1) -> Optional[List[MediaInfo]]:
"""
:param mtype: 媒体类型
:param sort_by: 排序方式
:param with_genres: 类型
:param with_original_language: 语言
:param with_keywords: 关键字
:param with_watch_providers: 提供商
:param vote_average: 评分
:param vote_count: 评分人数
:param release_date: 上映日期
:param page: 页码
:return: 媒体信息列表
"""
return self.run_module("tmdb_discover", mtype=mtype,
sort_by=sort_by, with_genres=with_genres,
sort_by=sort_by,
with_genres=with_genres,
with_original_language=with_original_language,
with_keywords=with_keywords,
with_watch_providers=with_watch_providers,
vote_average=vote_average,
vote_count=vote_count,
release_date=release_date,
page=page)
def tmdb_trending(self, page: int = 1) -> Optional[List[MediaInfo]]:

View File

@@ -326,7 +326,7 @@ class JobManager:
# 计算状态为完成的任务数
if __mediaid__ not in self._job_view:
return 0
return sum([task.fileitem.size for task in self._job_view[__mediaid__].tasks if task.state == "completed"])
return sum([task.fileitem.size for task in self._job_view[__mediaid__].tasks if task.state == "completed" and task.fileitem.size is not None])
def total(self) -> int:
"""

View File

@@ -1,3 +1,4 @@
import copy
import threading
import traceback
from typing import Any, Union, Dict, Optional
@@ -303,7 +304,7 @@ class Command(metaclass=Singleton):
)
else:
# 命令
cmd_data = command['data'] if command.get('data') else {}
cmd_data = copy.deepcopy(command['data']) if command.get('data') else {}
args_num = ObjectUtils.arguments(command['func'])
if args_num > 0:
if cmd_data:

View File

@@ -73,6 +73,18 @@ class SubscribeHistory(Base):
result = db.query(SubscribeHistory).filter(
SubscribeHistory.type == mtype
).order_by(
SubscribeHistory.date.desc()
SubscribeHistory.date.desc()
).offset((page - 1) * count).limit(count).all()
return list(result)
@staticmethod
@db_query
def exists(db: Session, tmdbid: int = None, doubanid: str = None, season: int = None):
if tmdbid:
if season:
return db.query(SubscribeHistory).filter(SubscribeHistory.tmdbid == tmdbid,
SubscribeHistory.season == season).first()
return db.query(SubscribeHistory).filter(SubscribeHistory.tmdbid == tmdbid).first()
elif doubanid:
return db.query(SubscribeHistory).filter(SubscribeHistory.doubanid == doubanid).first()
return None

View File

@@ -118,3 +118,16 @@ class SubscribeOper(DbOper):
kwargs.pop("id")
subscribe = SubscribeHistory(**kwargs)
subscribe.create(self._db)
def exist_history(self, tmdbid: int = None, doubanid: str = None, season: int = None):
"""
判断是否存在订阅历史
"""
if tmdbid:
if season:
return True if SubscribeHistory.exists(self._db, tmdbid=tmdbid, season=season) else False
else:
return True if SubscribeHistory.exists(self._db, tmdbid=tmdbid) else False
elif doubanid:
return True if SubscribeHistory.exists(self._db, doubanid=doubanid) else False
return False

View File

@@ -165,3 +165,12 @@ class BangumiModule(_ModuleBase):
if credits_info:
return [MediaInfo(bangumi_info=credit) for credit in credits_info]
return []
def bangumi_discover(self, **kwargs) -> Optional[List[MediaInfo]]:
"""
发现Bangumi番剧
"""
infos = self.bangumiapi.discover(**kwargs)
if infos:
return [MediaInfo(bangumi_info=info) for info in infos]
return []

View File

@@ -13,6 +13,7 @@ class BangumiApi(object):
"""
_urls = {
"discover": "v0/subjects",
"search": "search/subjects/%s?type=2",
"calendar": "calendar",
"detail": "v0/subjects/%s",
@@ -30,14 +31,17 @@ class BangumiApi(object):
@classmethod
@cached(maxsize=settings.CACHE_CONF["bangumi"], ttl=settings.CACHE_CONF["meta"])
def __invoke(cls, url, **kwargs):
def __invoke(cls, url, key: str = None, **kwargs):
req_url = cls._base_url + url
params = {}
if kwargs:
params.update(kwargs)
resp = cls._req.get_res(url=req_url, params=params)
try:
return resp.json() if resp else None
if not resp:
return None
result = resp.json()
return result.get(key) if key else result
except Exception as e:
print(e)
return None
@@ -194,3 +198,11 @@ class BangumiApi(object):
for item in result:
ret_list.append(item)
return ret_list
def discover(self, **kwargs):
"""
发现
"""
return self.__invoke(self._urls["discover"],
key="data",
_ts=datetime.strftime(datetime.now(), '%Y%m%d'), **kwargs)

View File

@@ -70,10 +70,13 @@ class Alist(StorageBase, metaclass=Singleton):
@cached(maxsize=1, ttl=60 * 60 * 24 * 2 - 60 * 5)
def __generate_token(self) -> str:
"""
使用账号密码生成一个临时token
如果设置永久令牌则返回永久令牌,否则使用账号密码生成一个临时 token
缓存2天提前5分钟更新
"""
conf = self.get_conf()
token = conf.get("token")
if token:
return str(token)
resp: Response = RequestUtils(headers={
'Content-Type': 'application/json'
}).post_res(

View File

@@ -356,26 +356,52 @@ class TheMovieDbModule(_ModuleBase):
return None
return self.scraper.get_metadata_img(mediainfo=mediainfo, season=season, episode=episode)
def tmdb_discover(self, mtype: MediaType, sort_by: str, with_genres: str, with_original_language: str,
def tmdb_discover(self, mtype: MediaType, sort_by: str,
with_genres: str,
with_original_language: str,
with_keywords: str,
with_watch_providers: str,
vote_average: float,
vote_count: int,
release_date: str,
page: int = 1) -> Optional[List[MediaInfo]]:
"""
:param mtype: 媒体类型
:param sort_by: 排序方式
:param with_genres: 类型
:param with_original_language: 语言
:param with_keywords: 关键字
:param with_watch_providers: 提供商
:param vote_average: 评分
:param vote_count: 评分人数
:param release_date: 发布日期
:param page: 页码
:return: 媒体信息列表
"""
if mtype == MediaType.MOVIE:
infos = self.tmdb.discover_movies(sort_by=sort_by,
with_genres=with_genres,
with_original_language=with_original_language,
page=page)
infos = self.tmdb.discover_movies({
"sort_by": sort_by,
"with_genres": with_genres,
"with_original_language": with_original_language,
"with_keywords": with_keywords,
"with_watch_providers": with_watch_providers,
"vote_average.gte": vote_average,
"vote_count.gte": vote_count,
"release_date.gte": release_date,
"page": page
})
elif mtype == MediaType.TV:
infos = self.tmdb.discover_tvs(sort_by=sort_by,
with_genres=with_genres,
with_original_language=with_original_language,
page=page)
infos = self.tmdb.discover_tvs({
"sort_by": sort_by,
"with_genres": with_genres,
"with_original_language": with_original_language,
"with_keywords": with_keywords,
"with_watch_providers": with_watch_providers,
"vote_average.gte": vote_average,
"vote_count.gte": vote_count,
"release_date.gte": release_date,
"page": page
})
else:
return []
if infos:

View File

@@ -1080,18 +1080,17 @@ class TmdbApi:
logger.error(str(e))
return {}
def discover_movies(self, **kwargs) -> List[dict]:
def discover_movies(self, params: dict) -> List[dict]:
"""
发现电影
:param kwargs:
:param params: 参数
:return:
"""
if not self.discover:
return []
try:
logger.debug(f"正在发现电影:{kwargs}...")
params_tuple = tuple(kwargs.items())
tmdbinfo = self.discover.discover_movies(params_tuple)
logger.debug(f"正在发现电影:{params}...")
tmdbinfo = self.discover.discover_movies(tuple(params.items()))
if tmdbinfo:
for info in tmdbinfo:
info['media_type'] = MediaType.MOVIE
@@ -1100,18 +1099,17 @@ class TmdbApi:
logger.error(str(e))
return []
def discover_tvs(self, **kwargs) -> List[dict]:
def discover_tvs(self, params: dict) -> List[dict]:
"""
发现电视剧
:param kwargs:
:param params: 参数
:return:
"""
if not self.discover:
return []
try:
logger.debug(f"正在发现电视剧:{kwargs}...")
params_tuple = tuple(kwargs.items())
tmdbinfo = self.discover.discover_tv_shows(params_tuple)
logger.debug(f"正在发现电视剧:{params}...")
tmdbinfo = self.discover.discover_tv_shows(tuple(params.items()))
if tmdbinfo:
for info in tmdbinfo:
info['media_type'] = MediaType.TV

View File

@@ -225,7 +225,8 @@ class _PluginBase(metaclass=ABCMeta):
return self.plugindata.del_data(plugin_id, key)
def post_message(self, channel: MessageChannel = None, mtype: NotificationType = None, title: str = None,
text: str = None, image: str = None, link: str = None, userid: str = None, username: str = None):
text: str = None, image: str = None, link: str = None, userid: str = None, username: str = None,
**kwargs):
"""
发送消息
"""
@@ -233,7 +234,7 @@ class _PluginBase(metaclass=ABCMeta):
link = settings.MP_DOMAIN(f"#/plugins?tab=installed&id={self.__class__.__name__}")
self.chain.post_message(Notification(
channel=channel, mtype=mtype, title=title, text=text,
image=image, link=link, userid=userid, username=username
image=image, link=link, userid=userid, username=username, **kwargs
))
def close(self):

View File

@@ -1,2 +1,2 @@
APP_VERSION = 'v2.2.4'
FRONTEND_VERSION = 'v2.2.4'
APP_VERSION = 'v2.2.5'
FRONTEND_VERSION = 'v2.2.5'