Skip to content

Commit

Permalink
lichess: handle player URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
Exirel committed Jul 25, 2021
1 parent 5c63d5e commit 561c64e
Show file tree
Hide file tree
Showing 7 changed files with 357 additions and 13 deletions.
43 changes: 40 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@ sopel-lichess
Lichess plugin for Sopel (Lichess URL handler). This plugin handle URL
from https://lichess.org for:

* games (done)
* players (TODO)
* TV channels (done)
* games
* players
* TV channels

Install
=======

The preferred way to install this plugin is through ``pip``::

$ pip install sopel-lichess

Note that you may need to use ``pip3``, depending on your system and your
installation.

Once this is done, you should configure and enable the plugin::

$ sopel-plugins configure lichess
$ sopel-plugins enable lichess

And then, restart your bot: this, again, depends on your system and how you run
your bot.

Lichess API Key
===============

This plugin uses the lichess.org API and requires an API key to work. Read
the `lichess's authentication documentation`__ to get your key.

.. __: https://lichess.org/api#section/Authentication

The plugin can be configured with the Sopel configuration wizard::

$ sopel-plugins configure lichess

Or manually, by editing your configuration and adding this section::

[lichess]
api_key = <YOUR API KEY>

Replace ``<YOUR API KEY>`` by your API key (no quote needed).
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ license = Eiffel Forum License, version 2
license_file = LICENSE.txt
platforms = Linux x86, x86-64
classifiers =
Development Status :: 3 - Alpha
Development Status :: 4 - Beta
Intended Audience :: Developers
Intended Audience :: System Administrators
License :: Eiffel Forum License (EFL)
Expand Down
46 changes: 44 additions & 2 deletions sopel_lichess/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import generator_stop

import unicodedata
from typing import List
from typing import List, cast

from sopel import formatting # type: ignore

Expand All @@ -11,10 +11,52 @@
WINNER = unicodedata.lookup('TROPHY')


def format_player(data: dict) -> str:
"""Format a player account ``data`` dict.
:return: formatted player's account information
"""
parts: List[str]

# names
name: str = cast(str, data.get('username', 'anonymous'))
title: str = cast(str, data.get('title'))
if title:
name = '%s %s' % (formatting.bold(title), name)

parts = [name]

# game count
count = data.get('count', {})
game_count = 'Played %s rated/%s' % (
count.get('rated', 0),
count.get('all', 0),
)
parts.append(game_count)

# game won
parts.append('%s %s' % (WINNER, count.get('win', 0)))

# following/followers
following = 'Following %d/%d' % (
data.get('nbFollowing', 0),
data.get('nbFollowers', 0),
)
parts.append(following)

# now playing
playing = data.get('playing')
if playing:
parts.append('Now playing: %s' % playing)

# join parts
return ' | '.join(parts)


def format_game_player(data: dict) -> str:
"""Format a game's player ``data`` dict.
:return: formatted player's information
:return: formatted player's game information
"""
rating = data.get('rating') or '???'
diff = int(data.get('ratingDiff') or '0')
Expand Down
17 changes: 14 additions & 3 deletions sopel_lichess/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
BASE_PATTERN = re.escape(r'https://lichess.org/')
MEMORY_KEY = '__sopel_lichess_api__'
LOCK = threading.Lock()
OUTPUT_PREFIX = '[lichess] '


def setup(bot: Sopel) -> None:
Expand Down Expand Up @@ -52,14 +53,24 @@ def configure(settings: Config) -> None:


@plugin.url(BASE_PATTERN + r'@/(?P<player_id>[^/\s]+)')
@plugin.output_prefix(OUTPUT_PREFIX)
def lichess_player(bot: SopelWrapper, trigger: Trigger) -> None:
"""Handle Lichess player's URL."""
player_id = trigger.group('player_id')
bot.say('Player: %s' % player_id)

with LOCK:
response = bot.memory[MEMORY_KEY].get(
'https://lichess.org/api/user/%s' % player_id,
headers={'Accept': 'application/json'})

if response.status_code == 200:
data = response.json()
result = parsers.format_player(data)
bot.say(result)


@plugin.url(BASE_PATTERN + r'(?P<game_id>[a-zA-Z0-9]{8})$')
@plugin.output_prefix('[lichess] ')
@plugin.output_prefix(OUTPUT_PREFIX)
def lichess_game(bot: SopelWrapper, trigger: Trigger) -> None:
"""Handle Lichess game's URL."""
game_id = trigger.group('game_id')
Expand All @@ -76,7 +87,7 @@ def lichess_game(bot: SopelWrapper, trigger: Trigger) -> None:


@plugin.url(BASE_PATTERN + r'tv/(?P<channel_id>[^/\s]+)$')
@plugin.output_prefix('[lichess] ')
@plugin.output_prefix(OUTPUT_PREFIX)
def lichess_tv_channel(bot: SopelWrapper, trigger: Trigger) -> None:
"""Handle Lichess TV channel's URL."""
channel_id = trigger.group('channel_id')
Expand Down
142 changes: 142 additions & 0 deletions tests/player.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{
"id": "georges",
"username": "Georges",
"online": true,
"perfs": {
"chess960": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"atomic": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"racingKings": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"ultraBullet": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"blitz": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"kingOfTheHill": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"bullet": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"correspondence": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"horde": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"puzzle": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"classical": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"rapid": {
"games": 2945,
"rating": 1609,
"rd": 60,
"prog": -22,
"prov": true
},
"storm": {
"runs": 44,
"score": 61
}
},
"createdAt": 1290415680000,
"disabled": false,
"tosViolation": false,
"profile": {
"country": "EC",
"location": "string",
"bio": "Free bugs!",
"firstName": "Thibault",
"lastName": "Duplessis",
"fideRating": 1500,
"uscfRating": 1500,
"ecfRating": 1500,
"links": "github.com/ornicar\r\ntwitter.com/ornicar"
},
"seenAt": 1522636452014,
"patron": true,
"playTime": {
"total": 3296897,
"tv": 12134
},
"language": "en-GB",
"title": "NM",
"url": "https://lichess.org/@/georges",
"playing": "https://lichess.org/yqfLYJ5E/black",
"nbFollowing": 299,
"nbFollowers": 2735,
"completionRate": 97,
"count": {
"all": 9265,
"rated": 7157,
"ai": 531,
"draw": 340,
"drawH": 331,
"loss": 4480,
"lossH": 4207,
"win": 4440,
"winH": 4378,
"bookmark": 71,
"playing": 6,
"import": 66,
"me": 0
},
"streaming": false,
"followable": true,
"following": false,
"blocking": false,
"followsYou": false
}
39 changes: 36 additions & 3 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,50 @@ def user(userfactory):
return userfactory('Exirel')


def test_player_url(irc, user):
def test_player_url(irc, user, requests_mock):
filename = os.path.join(os.path.dirname(__file__), 'player.json')
with open(filename, 'r') as fd:
body = fd.read()

requests_mock.get(
'https://lichess.org/api/user/georges',
status_code=200,
text=body,
headers={'Content-Type': 'application/json'},
)

irc.say(
user,
'#channel',
'Check my profile https://lichess.org/@/Master-Chess86 and battle me!',
'Check my profile https://lichess.org/@/georges and battle me!',
)

player = ' | '.join([
'%s %s' % (formatting.bold('NM'), 'Georges'),
'Played %d rated/%d' % (7157, 9265),
'%s 4440' % WINNER,
'Following %d/%d' % (299, 2735),
'Now playing: https://lichess.org/yqfLYJ5E/black',
])
assert irc.bot.backend.message_sent == rawlist(
'PRIVMSG #channel :Player: Master-Chess86',
'PRIVMSG #channel :[lichess] %s' % player,
)


def test_player_url_404(irc, user, requests_mock):
requests_mock.get(
'https://lichess.org/api/user/abcdefgh',
status_code=404,
)

irc.say(
user,
'#channel',
'Check my profile https://lichess.org/@/abcdefgh and battle me!',
)

assert not irc.bot.backend.message_sent


def test_game_url(irc, user, requests_mock):
requests_mock.get(
Expand Down
Loading

0 comments on commit 561c64e

Please sign in to comment.