Sync branch with master, fix conflict
This commit is contained in:
77
README.md
77
README.md
@@ -52,7 +52,7 @@ If you have any issues or you want to contribute, you are welcome! But please be
|
||||
|
||||
## Main differences from the original repository:
|
||||
|
||||
- Improve the logging
|
||||
- Improve the logging - Emoji, colors, file and soo on
|
||||
- Final report with all the data
|
||||
- Rewrite the entire code using classe instead of module with global variables
|
||||
- Automatic download the follower's list and use it as input
|
||||
@@ -172,8 +172,9 @@ pip install -r requirements.txt
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from colorama import Fore
|
||||
from TwitchChannelPointsMiner import TwitchChannelPointsMiner
|
||||
from TwitchChannelPointsMiner.logger import LoggerSettings
|
||||
from TwitchChannelPointsMiner.logger import LoggerSettings, ColorPalette
|
||||
from TwitchChannelPointsMiner.classes.Settings import Priority
|
||||
from TwitchChannelPointsMiner.classes.entities.Bet import Strategy, BetSettings, Condition, OutcomeKeys, FilterCondition
|
||||
from TwitchChannelPointsMiner.classes.entities.Streamer import Streamer, StreamerSettings
|
||||
@@ -192,7 +193,13 @@ twitch_miner = TwitchChannelPointsMiner(
|
||||
console_level=logging.INFO, # Level of logs - use logging.DEBUG for more info)
|
||||
file_level=logging.DEBUG, # Level of logs - If you think the log file it's too big, use logging.INFO
|
||||
emoji=True, # On Windows, we have a problem printing emoji. Set to false if you have a problem
|
||||
less=False # If you think that the logs are too verbose, set this to True
|
||||
less=False, # If you think that the logs are too verbose, set this to True
|
||||
colored=True, # If you want to print colored text
|
||||
color_palette=ColorPalette( # You can also create a custom palette color (for the common message).
|
||||
STREAMER_online="GREEN", # Don't worry about lower/upper case the script will be parse all the values.
|
||||
streamer_offline="red", # Read more in README.md
|
||||
BET_wiN=Fore.MAGENTA # Color allowed are: [BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET].
|
||||
)
|
||||
),
|
||||
streamer_settings=StreamerSettings(
|
||||
make_predictions=True, # If you want to Bet / Make prediction
|
||||
@@ -273,21 +280,59 @@ Available values are the following:
|
||||
You can combine all priority but keep in mind that use `ORDER` and `POINTS_ASCENDING` in the same settings doesn't make sense.
|
||||
|
||||
### LoggerSettings
|
||||
| Key | Type | Default | Description |
|
||||
|----------------- |----------------- |-------------------------------- |---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `save` | bool | True | If you want to save logs in file (suggested) |
|
||||
| `less` | bool | False | Reduce the logging format and message verbosity [#10](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/10) |
|
||||
| `console_level` | level | logging.INFO | Level of logs in terminal - Use logging.DEBUG for more helpful messages. |
|
||||
| `file_level` | level | logging.DEBUG | Level of logs in file save - If you think the log file it's too big, use logging.INFO |
|
||||
| `emoji` | bool | For Windows is False else True | On Windows, we have a problem printing emoji. Set to false if you have a problem |
|
||||
| Key | Type | Default | Description |
|
||||
|----------------- |----------------- |-------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `save` | bool | True | If you want to save logs in file (suggested) |
|
||||
| `less` | bool | False | Reduce the logging format and message verbosity [#10](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/10) |
|
||||
| `console_level` | level | logging.INFO | Level of logs in terminal - Use logging.DEBUG for more helpful messages. |
|
||||
| `file_level` | level | logging.DEBUG | Level of logs in file save - If you think the log file it's too big, use logging.INFO |
|
||||
| `emoji` | bool | For Windows is False else True | On Windows, we have a problem printing emoji. Set to false if you have a problem |
|
||||
| `colored` | bool | True | If you want to print colored text [#45](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/45) [#82](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/82) |
|
||||
| `color_palette` | ColorPalette | All messages are Fore.RESET except WIN and LOSE bet (GREEN and RED) | Create your custom color palette. Read more above. |
|
||||
|
||||
#### Color Palette
|
||||
Now you can customize the color of the terminal message. We have created a default ColorPalette that provide all the message with `DEFAULT (RESET)` color and the `BET_WIN` and `BET_LOSE` message `GREEN` and `RED` respectively.
|
||||
Currently you can only change the following types of messages:
|
||||
- `STREAMER_ONLINE`
|
||||
- `STREAMER_OFFLINE`
|
||||
- `GAIN_FOR_RAID`
|
||||
- `GAIN_FOR_CLAIM`
|
||||
- `GAIN_FOR_WATCH`
|
||||
- `BET_WIN`
|
||||
- `BET_LOSE`
|
||||
- `BET_REFUND`
|
||||
- `BET_FILTERS`
|
||||
- `BET_GENERAL`
|
||||
- `BET_FAILED`
|
||||
- `BET_START`
|
||||
|
||||
The colors allowed are all the Fore color from Colorama: `BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.`
|
||||
The script was developed to handle all the human error, lower-case upper case and more, but I want to suggest using the following code-style
|
||||
```python
|
||||
from colorama import Fore
|
||||
ColorPalette(
|
||||
"STREAMER_ONLINE" = Fore.GREEN,
|
||||
"STREAMER_OFFLINE" = Fore.RED,
|
||||
"GAIN_FOR_RAID" = Fore.YELLOW,
|
||||
"GAIN_FOR_CLAIM" = Fore.YELLOW,
|
||||
"GAIN_FOR_WATCH" = Fore.YELLOW,
|
||||
"BET_WIN" = Fore.GREEN,
|
||||
"BET_LOSE" = Fore.RED,
|
||||
"BET_REFUND" = Fore.RESET,
|
||||
"BET_FILTERS" = Fore.MAGENTA,
|
||||
"BET_GENERAL" = Fore.BLUE,
|
||||
"BET_FAILED" = Fore.RED,
|
||||
)
|
||||
```
|
||||
|
||||
### StreamerSettings
|
||||
| Key | Type | Default | Description |
|
||||
|-------------------- |------------- |-------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `make_predictions` | bool | True | Choose if you want to make predictions / bet or not |
|
||||
| `follow_raid` | bool | True | Choose if you want to follow raid +250 points |
|
||||
| Key | Type | Default | Description |
|
||||
|-------------------- |------------- |-------------------------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `make_predictions` | bool | True | Choose if you want to make predictions / bet or not |
|
||||
| `follow_raid` | bool | True | Choose if you want to follow raid +250 points |
|
||||
| `claim_drops` | bool | True | If this value is True, the script will increase the watch-time for the current game. With this, you are able to claim the drops from Twitch Inventory [#21](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/21) |
|
||||
| `watch_streak` | bool | True | Choose if you want to change a priority for these streamers and try to catch the Watch Streak event [#11](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/11) |
|
||||
| `bet` | BetSettings | | Rules to follow for the bet |
|
||||
| `watch_streak` | bool | True | Choose if you want to change a priority for these streamers and try to catch the Watch Streak event [#11](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/11) |
|
||||
| `bet` | BetSettings | | Rules to follow for the bet |
|
||||
### BetSettings
|
||||
| Key | Type | Default | Description |
|
||||
|-------------------- |----------------- |--------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -392,23 +392,36 @@ class Twitch(object):
|
||||
|
||||
logger.info(
|
||||
f"Going to complete bet for {event}",
|
||||
extra={"emoji": ":four_leaf_clover:"},
|
||||
extra={
|
||||
"emoji": ":four_leaf_clover:",
|
||||
"color": Settings.logger.color_palette.BET_GENERAL,
|
||||
},
|
||||
)
|
||||
if event.status == "ACTIVE":
|
||||
skip, compared_value = event.bet.skip()
|
||||
if skip is True:
|
||||
logger.info(
|
||||
f"Skip betting for the event {event}", extra={"emoji": ":pushpin:"}
|
||||
f"Skip betting for the event {event}",
|
||||
extra={
|
||||
"emoji": ":pushpin:",
|
||||
"color": Settings.logger.color_palette.BET_FILTERS,
|
||||
},
|
||||
)
|
||||
logger.info(
|
||||
f"Skip settings {event.bet.settings.filter_condition}, current value is: {compared_value}",
|
||||
extra={"emoji": ":pushpin:"},
|
||||
extra={
|
||||
"emoji": ":pushpin:",
|
||||
"color": Settings.logger.color_palette.BET_FILTERS,
|
||||
},
|
||||
)
|
||||
else:
|
||||
if decision["amount"] >= 10:
|
||||
logger.info(
|
||||
f"Place {_millify(decision['amount'])} channel points on: {event.bet.get_outcome(selector_index)}",
|
||||
extra={"emoji": ":four_leaf_clover:"},
|
||||
extra={
|
||||
"emoji": ":four_leaf_clover:",
|
||||
"color": Settings.logger.color_palette.BET_GENERAL,
|
||||
},
|
||||
)
|
||||
|
||||
json_data = copy.deepcopy(GQLOperations.MakePrediction)
|
||||
@@ -424,7 +437,10 @@ class Twitch(object):
|
||||
else:
|
||||
logger.info(
|
||||
f"Oh no! The event is not active anymore! Current status: {event.status}",
|
||||
extra={"emoji": ":disappointed_relieved:"},
|
||||
extra={
|
||||
"emoji": ":disappointed_relieved:",
|
||||
"color": Settings.logger.color_palette.BET_FAILED,
|
||||
},
|
||||
)
|
||||
|
||||
def claim_bonus(self, streamer, claim_id):
|
||||
|
||||
@@ -9,8 +9,7 @@ from dateutil import parser
|
||||
from TwitchChannelPointsMiner.classes.entities.EventPrediction import EventPrediction
|
||||
from TwitchChannelPointsMiner.classes.entities.Message import Message
|
||||
from TwitchChannelPointsMiner.classes.entities.Raid import Raid
|
||||
|
||||
# from TwitchChannelPointsMiner.classes.Exceptions import TimeBasedDropNotFound
|
||||
from TwitchChannelPointsMiner.classes.Settings import Settings
|
||||
from TwitchChannelPointsMiner.classes.TwitchWebSocket import TwitchWebSocket
|
||||
from TwitchChannelPointsMiner.constants import WEBSOCKET
|
||||
from TwitchChannelPointsMiner.utils import (
|
||||
@@ -188,7 +187,12 @@ class WebSocketsPool:
|
||||
reason_code = message.data["point_gain"]["reason_code"]
|
||||
logger.info(
|
||||
f"+{earned} → {ws.streamers[streamer_index]} - Reason: {reason_code}.",
|
||||
extra={"emoji": ":rocket:"},
|
||||
extra={
|
||||
"emoji": ":rocket:",
|
||||
"color": Settings.logger.color_palette.get(
|
||||
f"GAIN_FOR_{reason_code}"
|
||||
),
|
||||
},
|
||||
)
|
||||
ws.streamers[streamer_index].update_history(
|
||||
reason_code, earned
|
||||
@@ -274,7 +278,10 @@ class WebSocketsPool:
|
||||
|
||||
logger.info(
|
||||
f"Place the bet after: {start_after}s for: {ws.events_predictions[event_id]}",
|
||||
extra={"emoji": ":alarm_clock:"},
|
||||
extra={
|
||||
"emoji": ":alarm_clock:",
|
||||
"color": Settings.logger.color_palette.BET_START,
|
||||
},
|
||||
)
|
||||
|
||||
elif (
|
||||
@@ -325,7 +332,12 @@ class WebSocketsPool:
|
||||
)
|
||||
logger.info(
|
||||
f"{ws.events_predictions[event_id]} - Result: {result_type}, {action}: {points_prefix}{_millify(points_gained)}",
|
||||
extra={"emoji": ":bar_chart:"},
|
||||
extra={
|
||||
"emoji": ":bar_chart:",
|
||||
"color": Settings.logger.color_palette.get(
|
||||
f"BET_{result_type}"
|
||||
),
|
||||
},
|
||||
)
|
||||
ws.events_predictions[event_id].final_result = {
|
||||
"type": event_result["type"],
|
||||
|
||||
@@ -101,7 +101,13 @@ class Streamer(object):
|
||||
self.offline_at = time.time()
|
||||
self.is_online = False
|
||||
|
||||
logger.info(f"{self} is Offline!", extra={"emoji": ":sleeping:"})
|
||||
logger.info(
|
||||
f"{self} is Offline!",
|
||||
extra={
|
||||
"emoji": ":sleeping:",
|
||||
"color": Settings.logger.color_palette.STREAMER_OFFLINE,
|
||||
},
|
||||
)
|
||||
|
||||
def set_online(self):
|
||||
if self.is_online is False:
|
||||
@@ -109,7 +115,13 @@ class Streamer(object):
|
||||
self.is_online = True
|
||||
self.stream.init_watch_streak()
|
||||
|
||||
logger.info(f"{self} is Online!", extra={"emoji": ":partying_face:"})
|
||||
logger.info(
|
||||
f"{self} is Online!",
|
||||
extra={
|
||||
"emoji": ":partying_face:",
|
||||
"color": Settings.logger.color_palette.STREAMER_ONLINE,
|
||||
},
|
||||
)
|
||||
|
||||
def print_history(self):
|
||||
return ", ".join(
|
||||
|
||||
@@ -5,13 +5,15 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import emoji
|
||||
from colorama import Fore, init
|
||||
|
||||
from TwitchChannelPointsMiner.utils import remove_emoji
|
||||
|
||||
|
||||
class EmojiFormatter(logging.Formatter):
|
||||
def __init__(self, *, fmt, datefmt=None, print_emoji=True):
|
||||
class GlobalFormatter(logging.Formatter):
|
||||
def __init__(self, *, fmt, datefmt=None, print_emoji=True, print_colored=False):
|
||||
self.print_emoji = print_emoji
|
||||
self.print_colored = print_colored
|
||||
logging.Formatter.__init__(self, fmt=fmt, datefmt=datefmt)
|
||||
|
||||
def format(self, record):
|
||||
@@ -36,9 +38,62 @@ class EmojiFormatter(logging.Formatter):
|
||||
# Full remove using a method from utils.
|
||||
record.msg = remove_emoji(record.msg)
|
||||
|
||||
if self.print_colored and hasattr(record, "color"):
|
||||
record.msg = f"{record.color}{record.msg}"
|
||||
|
||||
return super().format(record)
|
||||
|
||||
|
||||
# Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
|
||||
class ColorPalette(object):
|
||||
STREAMER_ONLINE = Fore.RESET
|
||||
STREAMER_OFFLINE = Fore.RESET
|
||||
|
||||
GAIN_FOR_RAID = Fore.RESET
|
||||
GAIN_FOR_CLAIM = Fore.RESET
|
||||
GAIN_FOR_WATCH = Fore.RESET
|
||||
|
||||
BET_WIN = Fore.GREEN
|
||||
BET_LOSE = Fore.RED
|
||||
BET_REFUND = Fore.RESET
|
||||
BET_FILTERS = Fore.RESET
|
||||
BET_GENERAL = Fore.RESET
|
||||
BET_FAILED = Fore.RESET
|
||||
BET_START = Fore.RESET
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for k in kwargs:
|
||||
if k.upper() in dir(self) and getattr(self, k.upper()) is not None:
|
||||
if kwargs[k] in [
|
||||
Fore.BLACK,
|
||||
Fore.RED,
|
||||
Fore.GREEN,
|
||||
Fore.YELLOW,
|
||||
Fore.BLUE,
|
||||
Fore.MAGENTA,
|
||||
Fore.CYAN,
|
||||
Fore.WHITE,
|
||||
Fore.RESET,
|
||||
]:
|
||||
setattr(self, k.upper(), kwargs[k])
|
||||
elif kwargs[k].upper() in [
|
||||
"BLACK",
|
||||
"RED",
|
||||
"GREEN",
|
||||
"YELLOW",
|
||||
"BLUE",
|
||||
"MAGENTA",
|
||||
"CYAN",
|
||||
"WHITE",
|
||||
"RESET",
|
||||
]:
|
||||
setattr(self, k.upper(), getattr(Fore, kwargs[k].upper()))
|
||||
|
||||
def get(self, key):
|
||||
color = getattr(self, key.upper()) if key.upper() in dir(self) else None
|
||||
return Fore.RESET if color is None else color
|
||||
|
||||
|
||||
class LoggerSettings:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -47,22 +102,29 @@ class LoggerSettings:
|
||||
console_level: int = logging.INFO,
|
||||
file_level: int = logging.DEBUG,
|
||||
emoji: bool = platform.system() != "Windows",
|
||||
colored: bool = False,
|
||||
color_palette: ColorPalette = ColorPalette(),
|
||||
):
|
||||
self.save = save
|
||||
self.less = less
|
||||
self.console_level = console_level
|
||||
self.file_level = file_level
|
||||
self.emoji = emoji
|
||||
self.colored = colored
|
||||
self.color_palette = color_palette
|
||||
|
||||
|
||||
def configure_loggers(username, settings):
|
||||
if settings.colored is True:
|
||||
init(autoreset=True)
|
||||
|
||||
root_logger = logging.getLogger()
|
||||
root_logger.setLevel(logging.DEBUG)
|
||||
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(settings.console_level)
|
||||
console_handler.setFormatter(
|
||||
EmojiFormatter(
|
||||
GlobalFormatter(
|
||||
fmt=(
|
||||
"%(asctime)s - %(levelname)s - [%(funcName)s]: %(message)s"
|
||||
if settings.less is False
|
||||
@@ -72,9 +134,9 @@ def configure_loggers(username, settings):
|
||||
"%d/%m/%y %H:%M:%S" if settings.less is False else "%d/%m %H:%M:%S"
|
||||
),
|
||||
print_emoji=settings.emoji,
|
||||
print_colored=settings.colored,
|
||||
)
|
||||
)
|
||||
root_logger.addHandler(console_handler)
|
||||
|
||||
if settings.save is True:
|
||||
logs_path = os.path.join(Path().absolute(), "logs")
|
||||
@@ -85,13 +147,16 @@ def configure_loggers(username, settings):
|
||||
)
|
||||
file_handler = logging.FileHandler(logs_file, "w", "utf-8")
|
||||
file_handler.setFormatter(
|
||||
EmojiFormatter(
|
||||
logging.Formatter(
|
||||
fmt="%(asctime)s - %(levelname)s - %(name)s - [%(funcName)s]: %(message)s",
|
||||
datefmt="%d/%m/%y %H:%M:%S",
|
||||
print_emoji=settings.emoji,
|
||||
)
|
||||
)
|
||||
file_handler.setLevel(settings.file_level)
|
||||
|
||||
root_logger.addHandler(file_handler)
|
||||
root_logger.addHandler(console_handler)
|
||||
return logs_file
|
||||
return None
|
||||
else:
|
||||
root_logger.addHandler(console_handler)
|
||||
return None
|
||||
|
||||
11
example.py
11
example.py
@@ -1,8 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from colorama import Fore
|
||||
from TwitchChannelPointsMiner import TwitchChannelPointsMiner
|
||||
from TwitchChannelPointsMiner.logger import LoggerSettings
|
||||
from TwitchChannelPointsMiner.logger import LoggerSettings, ColorPalette
|
||||
from TwitchChannelPointsMiner.classes.Settings import Priority
|
||||
from TwitchChannelPointsMiner.classes.entities.Bet import Strategy, BetSettings, Condition, OutcomeKeys, FilterCondition
|
||||
from TwitchChannelPointsMiner.classes.entities.Streamer import Streamer, StreamerSettings
|
||||
@@ -21,7 +22,13 @@ twitch_miner = TwitchChannelPointsMiner(
|
||||
console_level=logging.INFO, # Level of logs - use logging.DEBUG for more info)
|
||||
file_level=logging.DEBUG, # Level of logs - If you think the log file it's too big, use logging.INFO
|
||||
emoji=True, # On Windows, we have a problem printing emoji. Set to false if you have a problem
|
||||
less=False # If you think that the logs are too verbose, set this to True
|
||||
less=False, # If you think that the logs are too verbose, set this to True
|
||||
colored=True, # If you want to print colored text
|
||||
color_palette=ColorPalette( # You can also create a custom palette color (for the common message).
|
||||
STREAMER_online="GREEN", # Don't worry about lower/upper case the script will be parse all the values.
|
||||
streamer_offline="red", # Read more in README.md
|
||||
BET_wiN=Fore.MAGENTA # Color allowed are: [BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET].
|
||||
)
|
||||
),
|
||||
streamer_settings=StreamerSettings(
|
||||
make_predictions=True, # If you want to Bet / Make prediction
|
||||
|
||||
@@ -7,3 +7,4 @@ emoji
|
||||
millify
|
||||
pre-commit
|
||||
flask
|
||||
colorama
|
||||
|
||||
86
settings.json
Normal file
86
settings.json
Normal file
@@ -0,0 +1,86 @@
|
||||
[
|
||||
{
|
||||
"settings": null,
|
||||
"username": "r_eqxxx"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "vhauss"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "carriedbyoujua"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "robertcrg"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "blu3berrycupcake"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "brbteabreak"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "nut9tv"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "joaoboavista"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "hyeaga"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "chaosmachinegr"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "malteseknight_"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "daniel_rusev"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "serwinterofficial"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "shadowfrax"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "boxbox"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "ash_on_lol"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "dyannatv"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "mammoth"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "bchillz"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "buddha"
|
||||
},
|
||||
{
|
||||
"settings": null,
|
||||
"username": "trausi"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user