stackl
Advanced tools
+1
-1
| Metadata-Version: 2.1 | ||
| Name: stackl | ||
| Version: 0.0.1 | ||
| Version: 0.0.2 | ||
| Summary: Python library for connecting to Stack Exchange chat | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/ArtOfCode-/stackl |
| Metadata-Version: 2.1 | ||
| Name: stackl | ||
| Version: 0.0.1 | ||
| Version: 0.0.2 | ||
| Summary: Python library for connecting to Stack Exchange chat | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/ArtOfCode-/stackl |
+105
-5
@@ -6,2 +6,3 @@ import sys | ||
| import os.path | ||
| import re | ||
| import pickle | ||
@@ -12,3 +13,3 @@ import json | ||
| from stackl.errors import LoginError, InvalidOperationError | ||
| from stackl.models import Room | ||
| from stackl.models import Room, Message | ||
| from stackl.events import Event | ||
@@ -18,3 +19,3 @@ from stackl.wsclient import WSClient | ||
| VERSION = '0.0.1' | ||
| VERSION = '0.0.2' | ||
@@ -47,2 +48,3 @@ | ||
| self._authed_servers = [] | ||
| self._ids = {} | ||
@@ -78,2 +80,10 @@ def login(self, email, password, **kwargs): | ||
| def id(self, server): | ||
| """ | ||
| Get the ID of the logged-in user on the specified server. | ||
| :param server: the chat server from which to return a user ID | ||
| :return: Integer | ||
| """ | ||
| return self._ids[server] | ||
| def join(self, room_id, server): | ||
@@ -121,3 +131,3 @@ """ | ||
| def send(self, content, room=None, server=None): | ||
| def send(self, content, room=None, room_id=None, server=None): | ||
| """ | ||
@@ -130,7 +140,26 @@ Send a message to the specified room. | ||
| """ | ||
| if room is None or server is None: | ||
| if (room is None and room_id is None) or server is None: | ||
| raise InvalidOperationError('Cannot send a message to a non-existent room or a non-existent server.') | ||
| # TODO | ||
| if "\n" not in content and len(content) > 500: | ||
| raise ValueError('Single-line messages must be a maximum of 500 chars long.') | ||
| room_id = room_id or room.id | ||
| for i in range(1, 3): | ||
| response = self.session.post('https://chat.{}/chats/{}/messages/new'.format(server, room_id), data={ | ||
| 'fkey': self._fkeys[server], | ||
| 'text': content | ||
| }) | ||
| if response.status_code == 200: | ||
| break | ||
| elif i == 3: | ||
| raise RuntimeError('Failed to send message. No, I don\'t know why.') | ||
| message_data = response.json() | ||
| parent_match = re.match(r'^:(\d+) ', content) | ||
| message = Message(server, message_id=message_data['id'], timestamp=message_data['time'], content=content, | ||
| room_id=room_id, user_id=self._ids[server], | ||
| parent_id=None if parent_match is None else parent_match[1]) | ||
| return message | ||
| def add_handler(self, handler, **kwargs): | ||
@@ -201,2 +230,3 @@ """ | ||
| statuses.append(True) | ||
| self._ids[server] = int(re.match(r'/users/(\d+)', topbar_links[0].get('href'))[1]) | ||
@@ -206,2 +236,8 @@ return len(statuses) == 3 and all(statuses) | ||
| def _on_message(self, data, server): | ||
| """ | ||
| Internal. Handler passed to WSClient to handle incoming websocket data before it reaches the client application. | ||
| :param data: the raw text data received from the websocket | ||
| :param server: the server on which the message was received | ||
| :return: None | ||
| """ | ||
| data = json.loads(data) | ||
@@ -220,1 +256,65 @@ events = [v['e'] for k, v in data.items() if k[0] == 'r' and 'e' in v] | ||
| threading.Thread(name='handler_runner', target=run_handler).start() | ||
| def _chat_post_fkeyed(self, server, path, data=None): | ||
| """ | ||
| Sends a POST request to chat to perform an action, automatically inserting the chat server and fkey. | ||
| :param server: the server on which to perform the action | ||
| :param path: the host-less path to send the request to | ||
| :return: requests.Response | ||
| """ | ||
| req_data = {'fkey': self._fkeys[server]} | ||
| if data is not None: | ||
| req_data.update(data) | ||
| return self.session.post('https://chat.{}{}'.format(server, path), data=req_data) | ||
| def toggle_star(self, message_id, server): | ||
| self._chat_post_fkeyed(server, '/messages/{}/star'.format(message_id)) | ||
| def star_count(self, message_id, server): | ||
| star_soup = BeautifulSoup(self.session.get('https://chat.{}/transcript/message/{}'.format(server, message_id)), | ||
| 'html.parser') | ||
| counter = star_soup.select('#message-{} .flash .star .times'.format(message_id)) | ||
| if len(counter) > 0: | ||
| return int(counter[0].text) | ||
| else: | ||
| return 0 | ||
| def star(self, message_id, server): | ||
| if not self.has_starred(message_id, server): | ||
| self.toggle_star(message_id, server) | ||
| def unstar(self, message_id, server): | ||
| if self.has_starred(message_id, server): | ||
| self.toggle_star(message_id, server) | ||
| def has_starred(self, message_id, server): | ||
| star_soup = BeautifulSoup(self.session.get('https://chat.{}/transcript/message/{}'.format(server, message_id)), | ||
| 'html.parser') | ||
| counter = star_soup.select('#message-{} .flash .stars'.format(message_id)) | ||
| return len(counter) > 0 and 'user-star' in counter[0].get('class') | ||
| def cancel_stars(self, message_id, server): | ||
| self._chat_post_fkeyed(server, '/messages/{}/unstar'.format(message_id)) | ||
| def delete(self, message_id, server): | ||
| self._chat_post_fkeyed(server, '/messages/{}/delete'.format(message_id)) | ||
| def edit(self, message_id, server, new_content): | ||
| self._chat_post_fkeyed(server, '/messages/{}'.format(message_id), data={'text': new_content}) | ||
| def toggle_pin(self, message_id, server): | ||
| self._chat_post_fkeyed(server, '/messages/{}/owner-star'.format(message_id)) | ||
| def pin(self, message_id, server): | ||
| if not self.is_pinned(message_id, server): | ||
| self.toggle_pin(message_id, server) | ||
| def unpin(self, message_id, server): | ||
| if self.is_pinned(message_id, server): | ||
| self.toggle_pin(message_id, server) | ||
| def is_pinned(self, message_id, server): | ||
| star_soup = BeautifulSoup(self.session.get('https://chat.{}/transcript/message/{}'.format(server, message_id)), | ||
| 'html.parser') | ||
| counter = star_soup.select('#message-{} .flash .stars'.format(message_id)) | ||
| return len(counter) > 0 and 'owner-star' in counter[0].get('class') |
+6
-3
@@ -51,3 +51,6 @@ import re | ||
| self.username = user_soup.select('.usercard-xxl .user-status')[0].text | ||
| self.bio = user_soup.select('.user-stats tr')[3].select('td')[-1].text | ||
| try: | ||
| self.bio = user_soup.select('.user-stats tr')[3].select('td')[-1].text | ||
| except IndexError: | ||
| self.bio = '' | ||
@@ -94,4 +97,4 @@ in_room_cards = user_soup.select('#user-roomcards-container .roomcard') | ||
| def create_delegate(method_name): | ||
| def delegate(client): | ||
| getattr(client, method_name)(self.id, self.server) | ||
| def delegate(client, *args): | ||
| getattr(client, method_name)(self.id, self.server, *args) | ||
@@ -98,0 +101,0 @@ return delegate |
@@ -38,1 +38,5 @@ import threading | ||
| self.ws.close() | ||
| def send(self, content): | ||
| if self.ws is not None: | ||
| self.ws.send(content) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
27610
21.12%568
18.83%