New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

stackl

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

stackl - pypi Package Compare versions

Comparing version
0.0.1
to
0.0.2
+1
-1
PKG-INFO
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

@@ -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')

@@ -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)