missionpanel 1.0.2__tar.gz → 1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {missionpanel-1.0.2 → missionpanel-1.1}/PKG-INFO +19 -16
  2. missionpanel-1.1/missionpanel/example/__init__.py +2 -0
  3. missionpanel-1.1/missionpanel/example/rsshub.py +46 -0
  4. missionpanel-1.1/missionpanel/example/ttrss.py +142 -0
  5. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel.egg-info/PKG-INFO +19 -16
  6. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel.egg-info/SOURCES.txt +3 -0
  7. {missionpanel-1.0.2 → missionpanel-1.1}/setup.cfg +4 -4
  8. {missionpanel-1.0.2 → missionpanel-1.1}/setup.py +1 -1
  9. {missionpanel-1.0.2 → missionpanel-1.1}/LICENSE +0 -0
  10. {missionpanel-1.0.2 → missionpanel-1.1}/README.md +0 -0
  11. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/__init__.py +0 -0
  12. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/handler/__init__.py +0 -0
  13. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/handler/handler.py +0 -0
  14. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/orm/__init__.py +0 -0
  15. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/orm/core.py +0 -0
  16. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/orm/handler.py +0 -0
  17. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/submitter/__init__.py +0 -0
  18. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/submitter/abc.py +0 -0
  19. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/submitter/asynchronous.py +0 -0
  20. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel/submitter/submitter.py +0 -0
  21. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel.egg-info/dependency_links.txt +0 -0
  22. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel.egg-info/requires.txt +0 -0
  23. {missionpanel-1.0.2 → missionpanel-1.1}/missionpanel.egg-info/top_level.txt +0 -0
@@ -1,16 +1,19 @@
1
- Metadata-Version: 2.1
2
- Name: missionpanel
3
- Version: 1.0.2
4
- Summary: A mission panel
5
- Home-page: https://github.com/yindaheng98/missionpanel
6
- Author: yindaheng98
7
- Author-email: yindaheng98@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
-
14
- # missionnel
15
-
16
- Just a mission panel.
1
+ Metadata-Version: 2.1
2
+ Name: missionpanel
3
+ Version: 1.1
4
+ Summary: A mission panel
5
+ Home-page: https://github.com/yindaheng98/missionpanel
6
+ Author: yindaheng98
7
+ Author-email: yindaheng98@gmail.com
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+
16
+ # missionnel
17
+
18
+ Just a mission panel.
19
+
@@ -0,0 +1,2 @@
1
+ from .rsshub import RSSHubSubmitter, RSSHubRootSubmitter, RSSHubSubitemSubmitter
2
+ from .ttrss import TTRSSClient, TTRSSSubmitter, TTRSSHubSubmitter, TTRRSSHubRootSubmitter, TTRRSSHubSubitemSubmitter
@@ -0,0 +1,46 @@
1
+ import abc
2
+ from typing import AsyncGenerator, List, Any
3
+ import httpx
4
+ from xml.etree import ElementTree
5
+ from missionpanel.submitter import AsyncSubmitter
6
+
7
+
8
+ class RSSHubSubmitter(AsyncSubmitter, metaclass=abc.ABCMeta):
9
+
10
+ @abc.abstractmethod
11
+ async def parse_xml(self, xml: str) -> AsyncGenerator[Any, None]:
12
+ pass
13
+
14
+ async def derive_tags(self, mission_content) -> List[str]:
15
+ return []
16
+
17
+ async def derive_matcher(self, mission_content) -> List[str]:
18
+ return [mission_content['url']]
19
+
20
+ async def create_missions(self, rsshub: str, **httpx_client_options):
21
+ async with httpx.AsyncClient(**httpx_client_options) as client:
22
+ response = await client.get(rsshub)
23
+ async for mission_content in self.parse_xml(response.text):
24
+ matchers = await self.derive_matcher(mission_content)
25
+ await self.create_mission(mission_content, matchers)
26
+ tags = await self.derive_tags(mission_content)
27
+ if len(tags) > 0:
28
+ await self.add_tags(matchers, tags)
29
+
30
+
31
+ class RSSHubRootSubmitter(RSSHubSubmitter):
32
+
33
+ async def parse_xml(self, xml: str) -> AsyncGenerator[str, None]:
34
+ root = ElementTree.XML(xml)
35
+ yield {
36
+ 'url': root.find('channel/link').text,
37
+ 'latest': [item.find('link').text for item in root.iter('item')][0]
38
+ }
39
+
40
+
41
+ class RSSHubSubitemSubmitter(RSSHubSubmitter):
42
+
43
+ async def parse_xml(self, xml: str) -> AsyncGenerator[str, None]:
44
+ root = ElementTree.XML(xml)
45
+ for item in root.find('channel').iter('item'):
46
+ yield {'url': item.find('link').text}
@@ -0,0 +1,142 @@
1
+ import abc
2
+ import asyncio
3
+ import json
4
+ import logging
5
+ from typing import Any, AsyncGenerator, Dict, List
6
+ import httpx
7
+ from xml.etree import ElementTree
8
+ from missionpanel.submitter import AsyncSubmitter
9
+
10
+
11
+ class TTRSSClient(httpx.AsyncClient):
12
+ """一个简单的异步TTRSS客户端"""
13
+ sem_list: Dict[str, asyncio.Semaphore] = {} # 同一时刻一个链接只能有一个客户端登录,这里用一个信号量列表控制
14
+
15
+ def __init__(self, url: str, username: str, password: str, **kwargs):
16
+ super().__init__(**kwargs)
17
+ self.__url = url
18
+ self.__username = username
19
+ self.__password = password
20
+ self.__logger = logging.getLogger("TTRSSClient")
21
+ self.__sid = None
22
+ if self.__url not in TTRSSClient.sem_list: # 给每个链接一个信号量
23
+ TTRSSClient.sem_list[self.__url] = None # 信号量必须在事件循环开始后生成,此处先给个标记
24
+
25
+ async def __aenter__(self):
26
+ for url in TTRSSClient.sem_list: # 信号量必须在事件循环开始后生成
27
+ if TTRSSClient.sem_list[url] is None: # 已经生成的信号量不要变
28
+ self.__logger.debug('semaphore for TTRSS API %s initialized' % url)
29
+ TTRSSClient.sem_list[url] = asyncio.Semaphore(1) # 生成信号量
30
+ await TTRSSClient.sem_list[self.__url].__aenter__() # 同一时刻一个链接只能有一个客户端登录
31
+ self.__logger.debug('semaphore for TTRSS API %s got' % self.__url)
32
+ await super().__aenter__()
33
+ self.__logger.debug('httpx cli for TTRSS API %s initialized' % self.__url)
34
+ try:
35
+ data = (await super().post(self.__url, content=json.dumps({
36
+ 'op': 'login',
37
+ 'user': self.__username,
38
+ 'password': self.__password
39
+ }))).json()
40
+ self.__logger.debug('TTRSS API login response: %s' % data)
41
+ self.__sid = data['content']['session_id']
42
+ self.__logger.debug('TTRSS API login successful, sid: %s' % self.__sid)
43
+ except Exception:
44
+ self.__logger.exception('TTRSS API login failed, error: ')
45
+ return self
46
+
47
+ async def __aexit__(self, *args, **kwargs):
48
+ try:
49
+ data = (await super().post(self.__url, content=json.dumps({
50
+ "sid": self.__sid,
51
+ "op": "logout"
52
+ }))).json()
53
+ self.__logger.debug('TTRSS API logout response: %s' % data)
54
+ self.__logger.debug('TTRSS API logout successful, sid: %s' % self.__sid)
55
+ except Exception:
56
+ self.__logger.exception('TTRSS API logout failed, error: ')
57
+ await super().__aexit__(*args, **kwargs)
58
+ await TTRSSClient.sem_list[self.__url].__aexit__(*args, **kwargs)
59
+ self.__logger.debug('semaphore for TTRSS API %s released' % self.__url)
60
+
61
+ async def api(self, data: dict):
62
+ data['sid'] = self.__sid
63
+ self.__logger.debug("post data to TTRSS API %s: %s" % (self.__url, data))
64
+ try:
65
+ return (await super().post(self.__url, content=json.dumps(data))).json()['content']
66
+ except Exception:
67
+ self.__logger.exception('TTRSS API post failed, error: ')
68
+ return None
69
+
70
+
71
+ class TTRSSSubmitter(AsyncSubmitter, metaclass=abc.ABCMeta):
72
+
73
+ @abc.abstractmethod
74
+ async def parse_content(self, feed: dict, content: dict) -> AsyncGenerator[Any, None]:
75
+ pass
76
+
77
+ async def derive_tags(self, mission_content) -> List[str]:
78
+ return []
79
+
80
+ async def derive_matcher(self, mission_content) -> List[str]:
81
+ return [mission_content['url']]
82
+
83
+ async def create_missions(self, url: str, username: str, password: str, cat_id: int, **httpx_client_options):
84
+ async with TTRSSClient(url, username, password, **httpx_client_options) as client:
85
+ feeds = await client.api({
86
+ "op": "getFeeds",
87
+ "cat_id": cat_id,
88
+ "limit": None
89
+ })
90
+ for feed in feeds:
91
+ content = await client.api({
92
+ "op": "getHeadlines",
93
+ "feed_id": feed['id'],
94
+ "limit": 1,
95
+ "view_mode": "all_articles",
96
+ "order_by": "feed_dates"
97
+ })
98
+ async for mission_content in self.parse_content(feed, content, **httpx_client_options):
99
+ matchers = await self.derive_matcher(mission_content)
100
+ await self.create_mission(mission_content, matchers)
101
+ tags = await self.derive_tags(mission_content)
102
+ if len(tags) > 0:
103
+ await self.add_tags(matchers, tags)
104
+
105
+
106
+ class TTRSSHubSubmitter(TTRSSSubmitter):
107
+ logger = logging.getLogger("TTRSSHubSubmitter")
108
+
109
+ @abc.abstractmethod
110
+ async def parse_xml(self, xml: str) -> AsyncGenerator[Any, None]:
111
+ pass
112
+
113
+ async def parse_content_nocatch(self, feed: dict, content: dict, **httpx_client_options) -> AsyncGenerator[Any, None]:
114
+ async with httpx.AsyncClient(**httpx_client_options) as client:
115
+ response = await client.get(feed['feed_url'])
116
+ async for mission_content in self.parse_xml(response.text):
117
+ yield mission_content
118
+
119
+ async def parse_content(self, feed: dict, content: dict, **httpx_client_options) -> AsyncGenerator[Any, None]:
120
+ try:
121
+ async for mission_content in self.parse_content_nocatch(feed, content, **httpx_client_options):
122
+ yield mission_content
123
+ except Exception as e:
124
+ self.logger.warning(f'parse content failed, error: {e}')
125
+
126
+
127
+ class TTRRSSHubRootSubmitter(TTRSSHubSubmitter):
128
+
129
+ async def parse_xml(self, xml: str) -> AsyncGenerator[str, None]:
130
+ root = ElementTree.XML(xml)
131
+ yield {
132
+ 'url': root.find('channel/link').text,
133
+ 'latest': [item.find('link').text for item in root.iter('item')][0]
134
+ }
135
+
136
+
137
+ class TTRRSSHubSubitemSubmitter(TTRSSHubSubmitter):
138
+
139
+ async def parse_xml(self, xml: str) -> AsyncGenerator[str, None]:
140
+ root = ElementTree.XML(xml)
141
+ for item in root.find('channel').iter('item'):
142
+ yield {'url': item.find('link').text}
@@ -1,16 +1,19 @@
1
- Metadata-Version: 2.1
2
- Name: missionpanel
3
- Version: 1.0.2
4
- Summary: A mission panel
5
- Home-page: https://github.com/yindaheng98/missionpanel
6
- Author: yindaheng98
7
- Author-email: yindaheng98@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
-
14
- # missionnel
15
-
16
- Just a mission panel.
1
+ Metadata-Version: 2.1
2
+ Name: missionpanel
3
+ Version: 1.1
4
+ Summary: A mission panel
5
+ Home-page: https://github.com/yindaheng98/missionpanel
6
+ Author: yindaheng98
7
+ Author-email: yindaheng98@gmail.com
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+
16
+ # missionnel
17
+
18
+ Just a mission panel.
19
+
@@ -7,6 +7,9 @@ missionpanel.egg-info/SOURCES.txt
7
7
  missionpanel.egg-info/dependency_links.txt
8
8
  missionpanel.egg-info/requires.txt
9
9
  missionpanel.egg-info/top_level.txt
10
+ missionpanel/example/__init__.py
11
+ missionpanel/example/rsshub.py
12
+ missionpanel/example/ttrss.py
10
13
  missionpanel/handler/__init__.py
11
14
  missionpanel/handler/handler.py
12
15
  missionpanel/orm/__init__.py
@@ -1,4 +1,4 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -12,7 +12,7 @@ package_dir = {
12
12
 
13
13
  setup(
14
14
  name='missionpanel',
15
- version='1.0.2',
15
+ version='1.1',
16
16
  author='yindaheng98',
17
17
  author_email='yindaheng98@gmail.com',
18
18
  url='https://github.com/yindaheng98/missionpanel',
File without changes
File without changes