Skip to content

Commit

Permalink
Merge pull request #36 from Tkd-Alex/dedicated-settings-refactory
Browse files Browse the repository at this point in the history
Dedicated settings for each streamer
  • Loading branch information
Tkd-Alex authored Feb 1, 2021
2 parents 2d1997b + 4bda13c commit e13cd4b
Show file tree
Hide file tree
Showing 22 changed files with 308 additions and 195 deletions.
51 changes: 37 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,13 @@ For the bet system the script use Selenium. Could be usefull understand how to M

import logging
from TwitchChannelPointsMiner import TwitchChannelPointsMiner
from TwitchChannelPointsMiner.classes.Logger import LoggerSettings
from TwitchChannelPointsMiner.classes.Bet import Strategy, BetSettings
from TwitchChannelPointsMiner.logger import LoggerSettings
from TwitchChannelPointsMiner.classes.entities.Bet import Strategy, BetSettings
from TwitchChannelPointsMiner.classes.entities.Streamer import Streamer, StreamerSettings
from TwitchChannelPointsMiner.classes.TwitchBrowser import Browser, BrowserSettings

twitch_miner = TwitchChannelPointsMiner(
username="your-twitch-username",
make_predictions=True, # If you want to Bet / Make prediction | The browser will never start
follow_raid=True, # Follow raid to obtain more points
watch_streak=True, # If a streamer go online change the priotiry of streamers array and catch the watch screak. Issue #11
drops_events=True, # If you want to auto claim game drops from Twitch inventory Issue #21
claim_drops_startup=False, # If you want to auto claim all drops from Twitch inventory on startup
logger_settings=LoggerSettings(
save=True, # If you want to save logs in file (suggested)
Expand All @@ -181,17 +178,43 @@ twitch_miner = TwitchChannelPointsMiner(
show=False, # Show the browser during bet else headless mode
do_screenshot=False, # Do screenshot during the bet
),
bet_settings=BetSettings(
strategy=Strategy.SMART, # Choose you strategy!
percentage=5, # Place the x% of your channel points
percentage_gap=20, # Gap difference between outcomesA and outcomesB (for SMART stragegy)
max_points=50000, # If the x percentage of your channel points is gt bet_max_points set this value
streamer_settings=StreamerSettings(
make_predictions=True, # If you want to Bet / Make prediction
follow_raid=True, # Follow raid to obtain more points
claim_drops=True, # We can't filter rewards base on stream. Set to False for skip viewing counter increase and you will never obtain a drop reward from this script. Issue #21
watch_streak=True, # If a streamer go online change the priotiry of streamers array and catch the watch screak. Issue #11
bet=BetSettings(
strategy=Strategy.SMART, # Choose you strategy!
percentage=5, # Place the x% of your channel points
percentage_gap=20, # Gap difference between outcomesA and outcomesB (for SMART stragegy)
max_points=50000, # If the x percentage of your channel points is gt bet_max_points set this value
)
)
)

# You can customize the settings for each streamer. If not settings was provided the script will use the streamer_settings from TwitchChannelPointsMiner.
# If no streamer_settings provided in TwitchChannelPointsMiner the script will use default settings.
# The streamers array can be a String -> username or Streamer instance.

# The settings priority are: settings in mine function, settings in TwitchChannelPointsMiner instance, default settings.
# For example if in the mine function you don't provide any value for 'make_prediction' but you have set it on TwitchChannelPointsMiner instance the script will take the value from here.
# If you haven't set any value even in the instance the default one will be used

twitch_miner.mine(
["streamer1", "streamer2"], # Array of streamers (order = priority)
followers=False # Automatic download the list of your followers
[
Streamer("streamer-username01", settings=StreamerSettings(make_predictions=True , follow_raid=False , claim_drops=True , watch_streak=True , bet=BetSettings(strategy=Strategy.SMART , percentage=5 , percentage_gap=20 , max_points=234 ) )),
Streamer("streamer-username02", settings=StreamerSettings(make_predictions=False , follow_raid=True , claim_drops=False , bet=BetSettings(strategy=Strategy.PERCENTAGE , percentage=5 , percentage_gap=20 , max_points=1234 ) )),
Streamer("streamer-username03", settings=StreamerSettings(make_predictions=True , follow_raid=False , watch_streak=True , bet=BetSettings(strategy=Strategy.SMART , percentage=5 , percentage_gap=30 , max_points=50000 ) )),
Streamer("streamer-username04", settings=StreamerSettings(make_predictions=False , follow_raid=True , watch_streak=True )),
Streamer("streamer-username05", settings=StreamerSettings(make_predictions=True , follow_raid=True , claim_drops=True , watch_streak=True , bet=BetSettings(strategy=Strategy.ODDS , percentage=7 , percentage_gap=20 , max_points=90 ) )),
Streamer("streamer-username06"),
Streamer("streamer-username07"),
Streamer("streamer-username08"),
"streamer-username09",
"streamer-username10",
"streamer-username11"
], # Array of streamers (order = priority)
followers=False # Automatic download the list of your followers (unable to set custom settings for you followers list)
)
```
You can also use all the default values except for your username obv. Short version:
Expand All @@ -218,7 +241,7 @@ If the browser are currently betting or wait for more data It's impossible to in
- **PERCENTAGE**: Select the option with the highest percentage based on odds (It's the same that show Twitch) - Should be the same of select LOWEST_ODDS
- **SMART**: If the majority in percent chose an option then follow the other users, otherwise choose the option with the highest odds

![Screenshot](./assets/prediction_screen.png)
![Screenshot](./assets/prediction.png)

Here a concrete example:

Expand Down
188 changes: 115 additions & 73 deletions TwitchChannelPointsMiner/TwitchChannelPointsMiner.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,26 @@
from collections import OrderedDict
from datetime import datetime

from TwitchChannelPointsMiner.classes.Bet import BetSettings
from TwitchChannelPointsMiner.classes.entities.PubsubTopic import PubsubTopic
from TwitchChannelPointsMiner.classes.entities.Streamer import (
Streamer,
StreamerSettings,
)
from TwitchChannelPointsMiner.classes.Exceptions import StreamerDoesNotExistException
from TwitchChannelPointsMiner.classes.Logger import LoggerSettings, configure_loggers
from TwitchChannelPointsMiner.classes.PubsubTopic import PubsubTopic
from TwitchChannelPointsMiner.classes.Streamer import Streamer
from TwitchChannelPointsMiner.classes.Settings import Settings
from TwitchChannelPointsMiner.classes.Twitch import Twitch
from TwitchChannelPointsMiner.classes.TwitchBrowser import (
BrowserSettings,
TwitchBrowser,
)
from TwitchChannelPointsMiner.classes.WebSocketsPool import WebSocketsPool
from TwitchChannelPointsMiner.utils import _millify, get_user_agent
from TwitchChannelPointsMiner.logger import LoggerSettings, configure_loggers
from TwitchChannelPointsMiner.utils import (
_millify,
at_least_one_value_in_settings_is,
get_user_agent,
set_default_settings,
)

# Suppress warning for urllib3.connectionpool (selenium close connection)
# Suppress also the selenium logger please
Expand All @@ -36,45 +44,44 @@ class TwitchChannelPointsMiner:
def __init__(
self,
username: str,
make_predictions: bool = True,
follow_raid: bool = True,
watch_streak: bool = False,
claim_drops_startup: bool = False,
drops_events: bool = False,
# Settings for logging and selenium as you can see.
# This settings will be global shared trought Settings class
logger_settings: LoggerSettings = LoggerSettings(),
browser_settings: BrowserSettings = BrowserSettings(),
bet_settings: BetSettings = BetSettings(),
# Default values for all streamers
streamer_settings: StreamerSettings = StreamerSettings(),
):
self.username = username
self.browser_settings = browser_settings
self.bet_settings = bet_settings

self.twitch = Twitch(
self.username, get_user_agent(self.browser_settings.browser)
)
# Set as globally config
Settings.logger = logger_settings
Settings.browser = browser_settings

# Init as default all the missing values
streamer_settings.default()
streamer_settings.bet.default()
Settings.streamer_settings = streamer_settings

user_agent = get_user_agent(browser_settings.browser)
self.twitch = Twitch(self.username, user_agent)

self.twitch_browser = None
self.follow_raid = follow_raid
self.watch_streak = watch_streak
self.drops_events = drops_events
self.claim_drops_startup = claim_drops_startup
self.streamers = []
self.events_predictions = {}
self.minute_watcher_thread = None
self.ws_pool = None

self.make_predictions = make_predictions

self.session_id = str(uuid.uuid4())
self.running = False
self.start_datetime = None
self.original_streamers = []

self.logger_settings = logger_settings
self.logs_file = configure_loggers(self.username, self.logger_settings)
self.logs_file = configure_loggers(self.username, logger_settings)

signal.signal(signal.SIGINT, self.end)
signal.signal(signal.SIGSEGV, self.end)
signal.signal(signal.SIGTERM, self.end)
for sign in [signal.SIGINT, signal.SIGSEGV, signal.SIGTERM]:
signal.signal(sign, self.end)

def mine(self, streamers: list = [], followers=False):
self.run(streamers, followers)
Expand All @@ -94,115 +101,150 @@ def run(self, streamers: list = [], followers=False):
if self.claim_drops_startup is True:
self.twitch.claim_all_drops_from_inventory()

# Clear streamers array
# Remove duplicate 3. Preserving Order: Use OrderedDict (askpython .com)
streamers = [streamer_name.lower().strip() for streamer_name in streamers]
streamers = list(OrderedDict.fromkeys(streamers))
streamers_name: list = []
streamers_dict: dict = {}

for streamer in streamers:
username = (
streamer.username
if isinstance(streamer, Streamer)
else streamer.lower().strip()
)
streamers_name.append(username)
streamers_dict[username] = streamer

if followers is True:
# Append at the end with lowest priority
followers_array = self.twitch.get_followers()
logger.info(
f"Loading {len(followers_array)} followers from your profile!",
f"Load {len(followers_array)} followers from your profile!",
extra={"emoji": ":clipboard:"},
)
streamers += [fw for fw in followers_array if fw not in streamers]
for username in followers_array:
if username not in streamers_dict:
streamers_dict[username] = username.lower().strip()
else:
followers_array = []

streamers_name = list(
OrderedDict.fromkeys(streamers_name + followers_array)
)

logger.info(
f"Loading data for {len(streamers)} streamers. Please wait...",
f"Loading data for {len(streamers_name)} streamers. Please wait...",
extra={"emoji": ":nerd_face:"},
)
for streamer_username in streamers:
for username in streamers_name:
time.sleep(random.uniform(0.3, 0.7))
streamer_username.lower().strip()
try:
channel_id = self.twitch.get_channel_id(streamer_username)
streamer = Streamer(
streamer_username,
channel_id,
less_printing=self.logger_settings.less,

if isinstance(streamers_dict[username], Streamer) is True:
streamer = streamers_dict[username]
else:
streamer = Streamer(username)

streamer.channel_id = self.twitch.get_channel_id(username)
streamer.settings = set_default_settings(
streamer.settings, Settings.streamer_settings
)
streamer.settings.bet = set_default_settings(
streamer.settings.bet, Settings.streamer_settings.bet
)

self.streamers.append(streamer)
except StreamerDoesNotExistException:
logger.info(
f"Streamer {streamer_username} does not exist",
f"Streamer {username} does not exist",
extra={"emoji": ":cry:"},
)

# Populate the streamers with default values.
# 1. Load channel points and auto-claim bonus
# 2. Check if streamers is online
# 3. Check if the user is a Streamer. In thi case you can't do prediction
for streamer in self.streamers:
time.sleep(random.uniform(0.3, 0.7))
self.twitch.load_channel_points_context(
streamer, less_printing=self.logger_settings.less
)
self.twitch.load_channel_points_context(streamer)
self.twitch.check_streamer_online(streamer)
self.twitch.viewer_is_mod(streamer)
if streamer.viewer_is_mod is True:
streamer.settings.make_predictions = False

self.original_streamers = copy.deepcopy(self.streamers)

if (
self.make_predictions is True
): # We need a browser to make predictions / bet
# If we have at least one streamer with settings = make_predictions True
make_predictions = at_least_one_value_in_settings_is(
self.streamers, "make_predictions", True
)
# We need a browser to make predictions / bet
if make_predictions is True:
self.twitch_browser = TwitchBrowser(
self.twitch.twitch_login.get_auth_token(),
self.session_id,
settings=self.browser_settings,
settings=Settings.browser,
)
self.twitch_browser.init()

self.minute_watcher_thread = threading.Thread(
target=self.twitch.send_minute_watched_events,
args=(
self.streamers,
self.watch_streak,
at_least_one_value_in_settings_is(
self.streamers, "watch_streak", True
),
),
)
# self.minute_watcher_thread.daemon = True
self.minute_watcher_thread.start()

self.ws_pool = WebSocketsPool(
twitch=self.twitch,
twitch_browser=self.twitch_browser,
browser=self.twitch_browser,
streamers=self.streamers,
bet_settings=self.bet_settings,
events_predictions=self.events_predictions,
less_printing=self.logger_settings.less,
)
topics = [

# Subscribe to community-points-user. Get update for points spent or gains
self.ws_pool.submit(
PubsubTopic(
"community-points-user-v1",
user_id=self.twitch.twitch_login.get_user_id(),
)
]
)

if self.drops_events is True:
topics.append(
# If we have at least one streamer with settings = claim_drops True
# Going to subscribe to user-drop-events. Get update for drop-progress
claim_drops = at_least_one_value_in_settings_is(
self.streamers, "claim_drops", True
)
if claim_drops is True:
self.ws_pool.submit(
PubsubTopic(
"user-drop-events",
user_id=self.twitch.twitch_login.get_user_id(),
),
)
)

if self.make_predictions is True:
topics.append(
# Going to subscribe to predictions-user-v1. Get update when we place a new prediction (confirm)
if make_predictions is True:
self.ws_pool.submit(
PubsubTopic(
"predictions-user-v1",
user_id=self.twitch.twitch_login.get_user_id(),
)
)

for streamer in self.streamers:
topics.append(PubsubTopic("video-playback-by-id", streamer=streamer))
self.ws_pool.submit(
PubsubTopic("video-playback-by-id", streamer=streamer)
)

if self.follow_raid is True:
topics.append(PubsubTopic("raid", streamer=streamer))
if streamer.settings.follow_raid is True:
self.ws_pool.submit(PubsubTopic("raid", streamer=streamer))

if self.make_predictions is True:
topics.append(
if streamer.settings.make_predictions is True:
self.ws_pool.submit(
PubsubTopic("predictions-channel-v1", streamer=streamer)
)

for topic in topics:
self.ws_pool.submit(topic)

while self.running:
time.sleep(random.uniform(20, 60))
# Do an external control for WebSocket. Check if the thread is running
Expand Down Expand Up @@ -243,21 +285,21 @@ def __print_report(self):
)

if self.make_predictions:
print("")
logger.info(f"{self.bet_settings}", extra={"emoji": ":bar_chart:"})
for event_id in self.events_predictions:
if self.events_predictions[event_id].bet_confirmed is True:
self.events_predictions[event_id].set_less_printing(False)
logger.info(
f"{self.events_predictions[event_id].streamer.bet.settings}",
extra={"emoji": ":bar_chart:"},
)
logger.info(
f"{self.events_predictions[event_id].print_recap()}",
extra={"emoji": ":bar_chart:"},
)
print("")

for streamer_index in range(0, len(self.streamers)):
self.streamers[streamer_index].set_less_printing(False)
logger.info(
f"{self.streamers[streamer_index]}, Total points gained (after farming - before farming): {_millify(self.streamers[streamer_index].channel_points - self.original_streamers[streamer_index].channel_points)}",
f"{repr(self.streamers[streamer_index])}, Total Points Gained (after farming - before farming): {_millify(self.streamers[streamer_index].channel_points - self.original_streamers[streamer_index].channel_points)}",
extra={"emoji": ":robot:"},
)
if self.streamers[streamer_index].history != {}:
Expand Down
Loading

0 comments on commit e13cd4b

Please sign in to comment.