Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: LmaoLover/chatango-lib
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: neokuze/chatango-lib
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.

Commits on Jan 20, 2024

  1. Enhanced Task Management with Timer and Repeat Options

    add_task Function Improvement: Timer and Repeat Options
    neokuze authored Jan 20, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    87ed843 View commit details
  2. Update README.md

    neokuze authored Jan 20, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    67d0197 View commit details
  3. Update README.md

    neokuze authored Jan 20, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    b428bf3 View commit details
  4. Update README.md

    neokuze authored Jan 20, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    9f7cea7 View commit details
  5. Update README.md

    neokuze authored Jan 20, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    e98aeb7 View commit details

Commits on Jan 25, 2024

  1. Update client.py

    Enhancements to Asynchronous Task Management
    neokuze authored Jan 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d96363e View commit details
  2. Update __init__.py

    neokuze authored Jan 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    7ee24bf View commit details
  3. Enhanced Task Management

    The `add_task` function has been updated to provide unique handling for tasks with timeouts or repeated functions.
    neokuze authored Jan 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4a0d325 View commit details
  4. Update client.py

    neokuze authored Jan 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    0b80bdc View commit details
  5. Update client.py

    neokuze authored Jan 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    7116c9e View commit details

Commits on Apr 30, 2024

  1. Add files via upload

    neokuze authored Apr 30, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    00690b4 View commit details
  2. fix and proper init

    neokuze authored Apr 30, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    58f7b64 View commit details

Commits on May 2, 2024

  1. Update room.py

    stable update fix.
    neokuze authored May 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    315d07c View commit details
  2. Update room.py

    neokuze authored May 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    dd91c02 View commit details
  3. added disconnect event

    better use of the exceptions used when the client is disconnected.
    neokuze authored May 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    e4df3ea View commit details
  4. Update room.py

    neokuze authored May 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    b838e4e View commit details
  5. Update __init__.py

    neokuze authored May 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    ac2ecbf View commit details
  6. Update README.md

    neokuze authored May 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    fdd22a9 View commit details
  7. fix mistyping

    neokuze authored May 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    69f1305 View commit details

Commits on May 10, 2024

  1. Update room.py

    neokuze authored May 10, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    3538118 View commit details
  2. Add files via upload

    minior fix
    neokuze authored May 10, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    f48fb71 View commit details

Commits on May 11, 2024

  1. Update example.py

    not simple example.py
    neokuze authored May 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    45cd12d View commit details
  2. Fixed Description.

    proper thanks to everyone <3
    neokuze authored May 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    86aa75f View commit details
  3. Update setup.py

    neokuze authored May 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    e782e97 View commit details

Commits on Jun 9, 2024

  1. Update handler.py

    neokuze authored Jun 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    6d16d15 View commit details

Commits on Jun 11, 2024

  1. minior fix

    proper close of sessions
    neokuze authored Jun 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    c0f0023 View commit details
  2. Add files via upload

    fixed close and fix mistyping
    neokuze authored Jun 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    334eee9 View commit details
  3. Update example.py

    neokuze authored Jun 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    85e4324 View commit details
  4. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    c22002a View commit details
  5. fix format videos

    neokuze authored Jun 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4a4f8eb View commit details
  6. minior change

    neokuze authored Jun 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    24ca920 View commit details
  7. Update __init__.py

    neokuze authored Jun 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d3c5e05 View commit details
  8. minior change

    neokuze authored Jun 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    0e5f58d View commit details
  9. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    b28e84b View commit details
  10. Update message.py

    neokuze authored Jun 11, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    5210dc9 View commit details

Commits on Jun 12, 2024

  1. Update user.py

    neokuze authored Jun 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    cc2d272 View commit details
  2. Update room.py

    neokuze authored Jun 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    33c0f99 View commit details
  3. minior change

    logging error fix mistyping
    neokuze authored Jun 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    c3500b0 View commit details
  4. minior change

    close_code now is showing if debug is on.
    neokuze authored Jun 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    985ad89 View commit details
  5. minior change

    neokuze authored Jun 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    5cc2bdd View commit details
  6. Update room.py

    +added a better way to cancel task in connection.
    - removed old errorcode logger (innecesary)
    neokuze authored Jun 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    7b63c45 View commit details
  7. Update room.py

    removed unused import
    neokuze authored Jun 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    1931017 View commit details

Commits on Jun 13, 2024

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    f0ab5c9 View commit details

Commits on Jun 16, 2024

  1. Update room.py

    neokuze authored Jun 16, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    cec5ccd View commit details
  2. Update room.py

    neokuze authored Jun 16, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    0298d1b View commit details

Commits on Jun 17, 2024

  1. use TLS/SSL for urls

    TheClonerx committed Jun 17, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    Copy the full SHA
    b987418 View commit details
  2. Merge pull request neokuze#6 from TheClonerx/fix-#5

    use TLS/SSL for urls
    neokuze authored Jun 17, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    832a5a3 View commit details
  3. minior fix

    updated version to latest
    neokuze authored Jun 17, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    64c4853 View commit details
  4. minior fix for wait_for

    neokuze authored Jun 17, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    6d6ca1f View commit details
  5. Update utils.py

    neokuze authored Jun 17, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    29b619a View commit details
Showing with 320 additions and 188 deletions.
  1. +39 −17 README.md
  2. +1 −1 chatango/__init__.py
  3. +17 −7 chatango/client.py
  4. +3 −0 chatango/exceptions.py
  5. +2 −2 chatango/handler.py
  6. +22 −29 chatango/message.py
  7. +25 −16 chatango/pm.py
  8. +76 −45 chatango/room.py
  9. +27 −7 chatango/user.py
  10. +36 −49 chatango/utils.py
  11. +30 −8 example.py
  12. +37 −0 pyproject.toml
  13. +2 −1 requirements.txt
  14. +3 −6 setup.py
56 changes: 39 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,49 @@
# chatango-lib
Chatango Library for Python 3.6+

First, Special thanks to Sweets, Linkkg(Megamaster12) and [TheClonerx](https://github.com/linkkg/megach.py)
A powerful asynchronous Chatango library with aiohttp, written in Python 3.6+

Based on "megach.py", [linkkg](https://github.com/linkkg/) + "CherryBlossom", [Sweets](https://github.com/sweets/) also she motivate me xdd
### Library is supposed to be similar in usage for megach.py users, but was based on cherryblossom.

##### Things to know
The lib is not finished. i will make changes and make optimizations when required
also i want to make some special lib with original content.
so when i can, i will remove the "copied content" and make it myself.
Any question can be [here](https://palaciodehielo.chatango.com/) *en/es*, whatever.
Special thanks to [Sweets](https://github.com/sweets/), [Linkkg](https://github.com/linkkg/megach.py), and [TheClonerx](https://github.com/theclonerx).

upd: I'm seeing the limit using just one session, thinking about making a version that sets connection per session. 2.0v
wait for it.
#### Required
1-Python 3.6+
Similar usage to "megach.py" by Linkkg & "CherryBlossom" by Sweets. Sweets also motivated me with async, & thanks to [LmaoLover](https://github.com/LmaoLover/) for their help.

2-Setuptools or pip for dependencies -> aiohttp
---
#### supposed to be working
##### Rooms/reconnect

3-A little knowledge of asyncio
##### PM/DM

### How to install
## Linux / Termux
##### Websocket raise exceptions on close ws.

---
##### Things to know (TO DO)
[☑️] Proper way to handle ws error.

[☑️] Disconnect event.

[ ] Reimplement reconnect events.

[☑️] Fixed PM youtube video message.

[ ] Documentation.

[ ] Example with aiofiles

---
**Requirements:**

1. Python 3.6+
2. Setuptools or pip for dependencies (e.g., aiohttp)
3. A little knowledge of asyncio
---
## How to install
### Linux / Termux
`git clone https://github.com/neokuze/chatango-lib && cd chatango-lib`
## wherever is the setup.py
### wherever is the setup.py
`$ pip install --user -r requirements.txt`

`$ pip install --user .`

### or pip install
`$ pip install chatango-lib`
2 changes: 1 addition & 1 deletion chatango/__init__.py
Original file line number Diff line number Diff line change
@@ -5,4 +5,4 @@
from .utils import *
from .user import *

__version__ = "1.4.0"
__version__ = "1.4.4"
24 changes: 17 additions & 7 deletions chatango/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import logging
from typing import Coroutine, Dict, List, Optional
from typing import Coroutine, Dict, List, Optional, Union
from asyncio import Future, Task

from .pm import PM
from .room import Room
@@ -15,7 +16,6 @@ class Client(EventHandler):
def __init__(
self, username: str = "", password: str = "", rooms: List[str] = [], pm=False
):
self._tasks: List[asyncio.Task] = []
self.running = False
self.rooms: Dict[str, Room] = {}
self.pm: Optional[PM] = None
@@ -24,11 +24,20 @@ def __init__(
self.username = username
self.password = password

self._tasks: List[asyncio.Task] = []
self._task_loops: List[asyncio.Task] = []


def __dir__(self):
return public_attributes(self)

def add_task(self, coro: Coroutine):
self._tasks.append(asyncio.create_task(coro))

def add_task(self, coro_or_future: Union[Coroutine, Future]):# TODO
task = asyncio.create_task(coro_or_future)
self._handle_task_options(task)

def _handle_task_options(self, task: Task):
self._tasks.insert(0, task)

def _prune_tasks(self):
self._tasks = [task for task in self._tasks if not task.done()]
@@ -104,12 +113,13 @@ def leave_room(self, room_name: str):
if room:
self.add_task(room.disconnect())

def stop(self):
async def stop(self):# this must be async.
if self.pm:
self.leave_pm()
await self.pm.disconnect()

for room_name in self.rooms:
self.leave_room(room_name)
room = self.get_room(room_name)
await room._disconnect()

async def enable_bg(self, active=True):
"""Enable background if available."""
3 changes: 3 additions & 0 deletions chatango/exceptions.py
Original file line number Diff line number Diff line change
@@ -16,3 +16,6 @@ class NotConnectedError(BaseRoomError):

class InvalidRoomNameError(BaseRoomError):
pass

class WebSocketClosure(Exception):
pass
4 changes: 2 additions & 2 deletions chatango/handler.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import inspect
import logging

import typing

class EventHandler:
async def on_event(self, event: str, *args, **kwargs):
async def on_event(self, event: str, *args: typing.Any, **kwargs: typing.Dict[str, typing.Any]):
if "ping" in event or "pong" in event:
return
if len(args) == 0:
51 changes: 22 additions & 29 deletions chatango/message.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,12 @@
import enum
from typing import Optional

from .utils import get_anon_name, _clean_message, _parseFont, public_attributes
from .utils import (
get_anon_name,
_clean_message,
_parseFont,
public_attributes
)
from .user import User


@@ -107,11 +112,11 @@ async def _process(room, args):
if n in ["None"]:
n = None
if not isinstance(n, type(None)):
name = get_anon_name(n, puid)
name = "!"+get_anon_name(n, puid)
else:
name = get_anon_name("", puid)
name = "!"+get_anon_name("", puid)
else:
name = tname
name = "#"+tname
else:
if n:
name_color = n
@@ -158,7 +163,7 @@ async def _process_pm(room, args):
msg.room = room
msg.user = user
msg.time = mtime
msg.body = body
msg.body = pm_format(user, " "+body+" ")
msg.raw = rawmsg
msg.styles = msg.user._styles
msg.styles._name_color = name_color
@@ -178,7 +183,7 @@ def message_cut(message, lenth):

def mentions(body, room):
t = []
for match in re.findall("(\s)?@([a-zA-Z0-9]{1,20})(\s)?", body):
for match in re.findall(r"(\s)?@([a-zA-Z0-9]{1,20})(\s)?", body):
for participant in room.userlist:
if participant.name.lower() == match[1].lower():
if participant not in t:
@@ -208,26 +213,14 @@ async def send_pm(self, message):
await self.send_message(message)


# def format_videos(user, pmmessage): pass #TODO TESTING
# msg = pmmessage
# tag = 'i'
# r = []
# for word in msg.split(' '):
# if msg.strip() != "":
# regx = re.compile(r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/(watch\?v=|embed/|v/|.+\?v=)?(?P<id>[A-Za-z0-9\-=_]{11})') #"<" + tag + "(.*?)>", msg)
# match = regx.match(word)
# w = "<g x{0._fontSize}s{0._fontColor}=\"{0._fontFace}\">".format(user)
# if match:
# seek = match.group('id')
# word = f"<i s=\"vid','//yt','{seek}\" w=\"126\" h=\"93\"/>{w}"
# r.append(word)
# else:
# if not r:
# r.append(w+word)
# else:
# r.append(word)
# count = len([x for x in r if x == w])
# print(count)

# print(r)
# return " ".join(r)
def pm_format(user: User, msg: str) -> str:
pattern = r'<i s="vid://yt:([A-Za-z0-9_-]{11})"[^>]*?>'
def replace_match(match):
video_id = match.group(1)
url = f" https://www.youtube.com/watch?v={video_id}"
return url
msg = re.sub(pattern, replace_match, msg)
for x in re.findall("http[s]?://[\S]+?.jpg", msg):
msg = msg.replace(x, '<i s="%s" w="70.45" h="125"/>' % x)
#w = f"<g x{user.styles._font_size}s{user.styles._font_color}=\"{user.styles._font_face}\">"
return msg
41 changes: 25 additions & 16 deletions chatango/pm.py
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import asyncio
import logging
import traceback
import html as html2
from typing import Optional

from .utils import get_token, gen_uid, public_attributes
@@ -78,23 +79,29 @@ async def _do_recv(self):
Receive and process data from the socket
"""
while self._recv:
data: bytes = await self._recv.read(2048)
if not self.connected:
break
if data:
data_str: str = data.decode()
if data_str == "\r\n\x00":
await self._do_process("pong")
try:
data: bytes = await self._recv.read(2048)
if not self.connected:
break
if data:
data_str: str = data.decode()
if data_str == "\r\n\x00":
await self._do_process("pong")
else:
cmds = data_str.split("\r\n\x00")
for cmd in cmds:
if cmd != "":
logger.debug(f" IN {cmd}")
await self._do_process(cmd)
else:
cmds = data_str.split("\r\n\x00")
for cmd in cmds:
if cmd != "":
logger.debug(f" IN {cmd}")
await self._do_process(cmd)
await self._disconnect()
break
except ConnectionResetError:
pass
except TimeoutError:
pass
else:
await self._disconnect()
break
await asyncio.sleep(0.0001)
await asyncio.sleep(0.0001)
await self.handler._call_event("pm_disconnect", self)

async def _do_process(self, recv: str):
@@ -195,14 +202,16 @@ async def listen(self, user_name: str, password: str, reconnect=False):
break
await asyncio.sleep(3)

async def send_message(self, target, message: str, use_html: bool = False):
async def send_message(self, target, message: str, html: bool = False):
if isinstance(target, User):
target = target.name
if self._silent > time.time():
await self.handler._call_event("pm_silent", message)
else:
if len(message) > 0:
message = message # format_videos(self.user, message)
if not html:
message = html2.escape(message, quote=False)
nc, fs, fc, ff = (
f"<n{self.user.styles.name_color}/>",
f"{self.user.styles.font_size}",
121 changes: 76 additions & 45 deletions chatango/room.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import aiohttp
import asyncio
import sys
import html
import html as html2
import sys
import time
import enum
@@ -20,14 +20,18 @@
get_anon_name,
_id_gen,
public_attributes,
upload_image
)
from .message import Message, MessageFlags, _process, message_cut
from .user import User, ModeratorFlags, AdminFlags
from .exceptions import AlreadyConnectedError, InvalidRoomNameError
from .exceptions import AlreadyConnectedError, InvalidRoomNameError, WebSocketClosure
from .handler import EventHandler

logger = logging.getLogger(__name__)
from aiohttp import ClientTimeout
from aiohttp.http_websocket import WebSocketError
from aiohttp.client_exceptions import ServerDisconnectedError, ServerTimeoutError

logger = logging.getLogger(__name__)

class RoomFlags(enum.IntFlag):
LIST_TAXONOMY = 1 << 0
@@ -62,6 +66,7 @@ def __init__(self, handler: EventHandler):

def _reset(self):
self._connected = False
self._session = None
self._first_command = True
self._connection: Optional[aiohttp.ClientWebSocketResponse] = None
self._recv_task: Optional[asyncio.Task] = None
@@ -72,29 +77,34 @@ def connected(self):
return self._connected

async def _connect(self, server: str):
try:
self._connection = await get_aiohttp_session().ws_connect(
f"ws://{server}:8080/", origin="http://st.chatango.com"
)
self._connected = True
self._recv_task = asyncio.create_task(self._do_recv())
self._ping_task = asyncio.create_task(self._do_ping())
except aiohttp.ClientError as e:
await self._disconnect()
logging.getLogger(__name__).error(f"Could not connect to {server}: {e}")
while True:
try:
self._session = aiohttp.ClientSession()
self._connection = await self._session.ws_connect(
f"wss://{server}:8081/", origin="https://st.chatango.com"
)
self._connected = True
self._recv_task = asyncio.create_task(self._do_recv())
self._ping_task = asyncio.create_task(self._do_ping())
break #/ if connected will end & break the loop
except (aiohttp.ClientError, ConnectionResetError, ServerDisconnectedError, ServerTimeoutError) as e:
logging.getLogger(__name__).error(f"Could not connect to {server}: {e}")
await asyncio.sleep(5) # persist to connect!

async def _disconnect(self):
if self._ping_task:
self._ping_task.cancel()
if self._connection:
await self._connection.close()
if self._session:
await self._session.close()
self._reset()

async def _send_command(self, *args, terminator="\r\n\0"):
message = ":".join(args) + terminator
if message != "\r\n\x00": # ping
logger.debug(f'OUT {":".join(args)}')
if self._connection:
if self._connection and not self._connection._closing:
await self._connection.send_str(message)

async def _do_ping(self):
@@ -111,29 +121,40 @@ async def _do_ping(self):
except asyncio.exceptions.CancelledError:
pass

async def _do_recv(self):
async def _do_recv(self): # TODO implement reconection events, and proper disconnect
timeout = ClientTimeout(sock_connect=300,sock_read=300)
while self._connection:
message = await self._connection.receive()
if not self.connected:
break
if message.type == aiohttp.WSMsgType.TEXT:
if message.data:
logger.debug(f" IN {message.data}")
await self._do_process(message.data)
else:
await self._do_process("pong")
elif (
message.type == aiohttp.WSMsgType.CLOSE
or message.type == aiohttp.WSMsgType.CLOSING
or message.type == aiohttp.WSMsgType.CLOSED
or message.type == aiohttp.WSMsgType.ERROR
):
await self._disconnect()
break
else:
logger.error(f"Unexpected aiohttp.WSMsgType: {message.type}")
await asyncio.sleep(0.0001)
await self.handler._call_event("disconnect", self)
try:
message = await asyncio.wait_for(self._connection.receive(), timeout=timeout.total)
if not self.connected:
break
if message.type == aiohttp.WSMsgType.TEXT:
if message.data:
logger.debug(f" IN {message.data}")
await self._do_process(message.data)
else:
await self._do_process("pong")
elif (
message.type in [aiohttp.WSMsgType.CLOSE,
aiohttp.WSMsgType.CLOSING,aiohttp.WSMsgType.CLOSED]
):
raise WebSocketClosure
elif message.type == aiohttp.WSMsgType.ERROR:
logger.debug(f"[ws: {self._name}] Error from {message}")
raise WebSocketClosure
except (ConnectionResetError, WebSocketClosure, ServerTimeoutError,
ServerDisconnectedError, WebSocketError, asyncio.CancelledError) as e:
if isinstance(e, asyncio.CancelledError):
logger.debug("WebSocket listener task cancelled")
break
if self._connection and self._connection.closed:# no more logs
break
except (asyncio.TimeoutError, TimeoutError): #python 3.11+ for wait_for
await asyncio.sleep(5)
break #wait_for timeout cancel futures
await self._disconnect()
await self.handler._call_event("disconnect", self) #/ now will trigger a on_disconnect event


async def _do_process(self, recv: str):
"""
@@ -328,14 +349,14 @@ async def _login(self, user_name: str = "", password: str = ""):
async def _logout(self):
await self._send_command("blogout")

async def send_message(self, message, use_html=False, flags=None):
async def send_message(self, message, html=False, flags=None):
if not self.silent:
message_flags = (
flags if flags else self.message_flags + self.badge or 0 + self.badge
)
msg = str(message)
if not use_html:
msg = html.escape(msg, quote=False)
if not html:
msg = html2.escape(msg, quote=False)
msg = msg.replace("\n", "\r").replace("~", "&#126;")
for msg in message_cut(msg, self._maxlen):
message = f'<n{self.user.styles.name_color}/><f x{self.user.styles.font_size}{self.user.styles.font_color}="{self.user.styles.font_face}">{msg}</f>'
@@ -476,6 +497,9 @@ async def clear_all(self):
else:
return False

async def upload_img(self, files, return_url=True):
return await upload_image(self, files, return_url)

async def clear_user(self, user):
# TODO
if self.get_level(self.user) > 0:
@@ -554,7 +578,7 @@ async def set_bg_mode(self, mode):
self._bgmode = mode
if self.connected:
await self._send_command("getpremium", "l")
if self.user.ispremium:
if self.user and self.user.ispremium:
await self._send_command("msgbg", str(self._bgmode))

async def _style_init(self, user):
@@ -651,7 +675,8 @@ async def _rcmd_premium(self, args): # TODO
if self._bgmode and (
args[0] == "210" or (isinstance(self, Room) and self.owner == self.user)
):
self.user._ispremium = True
if self.user:
self.user._ispremium = True
await self._send_command("msgbg", str(self._bgmode))

async def _rcmd_show_fw(self, args=None):
@@ -687,9 +712,9 @@ async def _rcmd_g_participants(self, args):
if str(name) == "None":
isanon = True
if str(tname) != "None":
name = tname
name = "#"+tname
else:
name = get_anon_name(contime, puid)
name = "!"+get_anon_name(contime, puid)
user = User(name, isanon=isanon, puid=puid)
if user in ({self.owner} | self.mods):
user.setName(name)
@@ -707,9 +732,9 @@ async def _rcmd_participant(self, args):
isanon = False
if name == "None":
if tname != "None":
name = tname
name = "#"+tname
else:
name = get_anon_name(contime, puid)
name = "!"+get_anon_name(contime, puid)
isanon = True
user = User(name, isanon=isanon, puid=puid, ip=unknown)
user.setName(name)
@@ -994,3 +1019,9 @@ async def _rcmd_reload_profile(self, args):
user = User.get(args[0])
user._profile = None
await self.handler._call_event("profile_reload", user)

async def _rcmd_allunblocked(self, args):
self._banlist.clear()

async def _rcmd_groupflagstoggled(self, args):
pass
34 changes: 27 additions & 7 deletions chatango/user.py
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ class ModeratorFlags(enum.IntFlag):
MOD_ICON_VISIBLE = 1 << 17
IS_STAFF = 1 << 18
STAFF_ICON_VISIBLE = 1 << 19
UNBAN_ALL = 1 << 20 # ubna


AdminFlags = (
@@ -81,19 +82,27 @@ def __repr__(self):

@property
def age(self):
return self.styles._profile["about"]["age"]
return self.styles._profile["about"].get('age', '')

@property
def body(self):
return self.styles._profile["about"].get('body', '').replace('\r\n', '\n')

@property
def last_change(self):
return self.styles._profile["about"]["last_change"]
return self.styles._profile["about"].get('last_change', '')

@property
def gender(self):
return self.styles._profile["about"]["gender"]
return self.styles._profile["about"].get('gender', '')

@property
def location(self):
return self.styles._profile["about"]["location"]
return self.styles._profile["about"].get('location', '')

@property
def premium(self):
return self.styles._profile["about"].get('premium', 0)

@property
def get_user_dir(self):
@@ -128,11 +137,11 @@ def thumb(self):

@property
def _fp(self):
return "http://fp.chatango.com/profileimg"
return "https://fp.chatango.com/profileimg"

@property
def _ust(self):
return "http://ust.chatango.com/profileimg"
return "https://ust.chatango.com/profileimg"

@property
def name(self):
@@ -174,7 +183,9 @@ def setName(self, val):
def del_profile(self):
if self.styles.profile:
del self._styles._profile
self._styles._profile.update(dict(about={}, full={}))
self._styles._profile.update(dict(
about=dict(age="", last_change="", gender="?", location="", premium=0, body=""),
full=dict()))

def addSessionId(self, room, sid):
if room not in self._sids:
@@ -259,6 +270,15 @@ async def get_main_profile(self):
)
self._styles._profile["about"]["last_change"] = last_change

last_premium_start = about.find("<d>")
last_premium_end = about.find("</d>", last_premium_start)
last_premium = (
about[last_premium_start + 3 : last_premium_end]
if last_premium_start != -1
else ""
)
self._styles._profile["about"]["premium"] = int(last_premium)

if last_change:
age = abs(
datetime.datetime.now().year - int(last_change.split("-")[0])
85 changes: 36 additions & 49 deletions chatango/utils.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@
import logging
from typing import Tuple

import aiofiles

# fmt: off
specials = {
'mitvcanal': 56, 'animeultimacom': 34, 'cricket365live': 21,
@@ -104,17 +106,18 @@ def get_aiohttp_session():


async def get_token(user_name, passwd):
chatango, token = ["http://chatango.com/login", "auth.chatango.com"], None
chatango, token = ["https://chatango.com/login", "auth.chatango.com"], None
payload = {
"user_id": str(user_name).lower(),
"password": str(passwd),
"storecookie": "on",
"checkerrors": "yes",
}
# Use fresh session to retrieve auth cookies
async with aiohttp.ClientSession().post(chatango[0], data=payload) as resp:
if chatango[1] in resp.cookies:
token = str(resp.cookies[chatango[1]]).split("=")[1].split(";")[0]
async with aiohttp.ClientSession() as session:
async with session.post(chatango[0], data=payload) as resp:
if chatango[1] in resp.cookies:
token = str(resp.cookies[chatango[1]]).split("=")[1].split(";")[0]
return token


@@ -166,35 +169,34 @@ def escape_quote(s):
}
return body, headers

# async def upload_image(self, path, return_url=False):
# if self.user.isanon:
# return None
# with open(path, mode="rb") as f:
# files = {
# "filedata": {"filename": path, "content": f.read().decode("latin-1")}
# }
# data, headers = multipart(
# dict(u=self.client._default_user_name, p=self.client._default_password),
# files,
# )
# headers.update({"host": "chatango.com", "origin": "http://st.chatango.com"})
# async with get_aiohttp_session.post(
# "http://chatango.com/uploadimg",
# data=data.encode("latin-1"),
# headers=headers,
# ) as resp:
# response = await resp.text()
# if "success" in response:
# success = response.split(":", 1)[1]
# if success != None:
# if return_url:
# url = "http://ust.chatango.com/um/{}/{}/{}/img/t_{}.jpg"
# return url.format(
# self.user.name[0], self.user.name[1], self.user.name, success
# )
# else:
# return f"img{success}"
# return None
async def upload_image(self, path, return_url=False):
if self.user.isanon:
return None
async with aiofiles.open(path, mode="rb") as f:
content = await f.read()
files = {
"filedata": {"filename": path, "content": content.decode("latin-1")}
}
success = None
data, headers = multipart(
dict(u=self.handler.username, p=self.handler.password),
files,
)
headers.update({"host": "chatango.com", "origin": "https://st.chatango.com"})
async with get_aiohttp_session() as session:
async with session.post("https://chatango.com/uploadimg",data=data.encode("latin-1"), headers=headers) as resp:
response = await resp.text()
if "success" in response:
success = response.split(":", 1)[1]
if success != None:
if return_url:
url = "https://ust.chatango.com/um/{}/{}/{}/img/t_{}.jpg"
return url.format(
self.user.name[0], self.user.name[1], self.user.name, success
)
else:
return f"img{success}"
return None


async def sessionget(session: aiohttp.ClientSession, url: str):
@@ -312,21 +314,6 @@ def _parseFont(f: str, pm=False) -> Tuple[str, str, str]:
return match.groups()


def _videoImagePMFormat(text):
"""Returns text with formatted video and image for PM sending"""
for x in re.findall("(http[s]?://[^\s]+outube.com/watch\?v=([^\s]+))", text):
original = x[0]
cambio = '<i s="vid://yt:%s" w="126" h="96"/>' % x[1]
text = text.replace(original, cambio)
for x in re.findall("(http[s]?://[\S]+outu.be/([^\s]+))", text):
original = x[0]
cambio = '<i s="vid://yt:%s" w="126" h="96"/>' % x[1]
text = text.replace(original, cambio)
for x in re.findall("http[s]?://[\S]+?.jpg", text):
text = text.replace(x, '<i s="%s" w="70.45" h="125"/>' % x)
return text


class Styles:
def __init__(
self,
@@ -354,7 +341,7 @@ def __init__(
"useimg": "0",
}
self._profile = dict(
about=dict(age="", last_change="", gender="?", location="", d="", body=""),
about=dict(age="", last_change="", gender="?", location="", premium=0, body=""),
full=dict(),
)

38 changes: 30 additions & 8 deletions example.py
Original file line number Diff line number Diff line change
@@ -11,30 +11,52 @@ class config:
rooms = ["asynclibraryinpython"]
pm = False

class MyBot(chatango.Client):
async def on_connect(self, room: typing.Union[chatango.Room, chatango.PM]):
class Bot(chatango.Client):
async def on_connect(self, room):
print("[info] Connected to {}".format(repr(room)))

await room.set_bg_mode(1) #enable premium bg.

async def on_pm_connect(self, pm):
print("[info] Connected to {}".format(repr(pm)))
await pm.enable_bg() #enable premium bg for pm

async def on_disconnect(self, room):
print("[info] Disconnected from {}".format(repr(room)))
async def on_message(self, message):

async def on_pm_disconnect(self, pm):
print("[info] Disconnected from {}".format(repr(pm)))

async def on_message(self, message):
print(time.strftime("%b/%d-%H:%M:%S", time.localtime(message.time)),
message.room.name, message.user.showname, ascii(message.body)[1:-1])
if message.body.split()[0] in ["!a"]:
await message.room.send_message("test", html=True)

async def on_pm_message(self, message):
print(time.strftime("%b/%d-%H:%M:%S", time.localtime(message.time)),
message.room.name, message.user.showname, ascii(message.body)[1:-1])
if message.body.split(' ')[0] in ["!a"]:
await message.room.send_message(message.user.name, "test")


"""
Specials thanks to LmaoLover, TheClonerx
"""
bot = Bot(config.user_name, config.passwd, config.rooms , pm=config.pm)
async def main():
await bot.run(forever=True)

async def stop():
await bot.stop()

if __name__ == "__main__":
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
bot = Bot(config.user_name, config.passwd, config.rooms , pm=config.pm)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(bot.run(forever=True))
loop.run_until_complete(main())
loop.run_forever()
except KeyboardInterrupt:
print("[KeyboardInterrupt] Killed bot.")
finally:
loop.run_until_complete(stop())
loop.close()
37 changes: 37 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "chatango"
version = "1.4.4"
requires-python = ">=3.8"
description = "Another powerful async library for chatango."
authors = [
{ name = "Yado", email = "imneokuze@gmail.com" },
{ name = "José", email = "contact@theclonerx.me" }
]
license = { file = "LICENSE" }
readme = "README.md"
keywords = ["chatango-lib", "chatango"]
classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GPL3.0 License",
"Operating System :: OS Independent",
]
dependencies = [
"aiohttp>=3.9.5",
"aiofiles>=23.2.1"
]

[project.urls]
homepage = "https://neokuze.github.io/chatango-lib"
repository = "https://github.com/neokuze/chatango-lib"

[tool.setuptools]
packages = ["chatango"]
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
aiohttp # http requests and websockets
aiohttp # http requests and websockets
aiofiles # for opening files
9 changes: 3 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from setuptools import setup
import re

requirements = []

with open("requirements.txt") as f:
requirements = f.read().splitlines()
requirements = ["aiohttp>=3.9.5", "aiofiles>=23.2.1"]

version = ''
with open('chatango/__init__.py') as f:
@@ -17,7 +14,7 @@
readme = f.read()

setup(name="chatango-lib",
author="Yado",
author="neokuze",
url="https://github.com/neokuze/chatango-lib",
version=version,
packages=["chatango"],
@@ -26,5 +23,5 @@
long_description=readme,
long_description_content_type="text/markdown",
install_requires=requirements,
python_requires=">=3.6"
python_requires=">=3.8"
)