From 9cfcb0d28ca49e59dd55c3c9389284f7724ea2ef Mon Sep 17 00:00:00 2001 From: Majjo Duran Date: Tue, 17 Feb 2026 14:51:34 +0100 Subject: [PATCH] Remove betting/prediction system - focus on drops only --- README.md | 192 +--------- .../TwitchChannelPointsMiner.py | 50 --- TwitchChannelPointsMiner/classes/Settings.py | 7 - TwitchChannelPointsMiner/classes/Twitch.py | 83 ----- .../classes/WebSocketsPool.py | 152 +------- .../classes/entities/Bet.py | 345 ------------------ .../classes/entities/EventPrediction.py | 94 ----- .../classes/entities/Streamer.py | 24 +- example.py | 38 +- 9 files changed, 33 insertions(+), 952 deletions(-) delete mode 100644 TwitchChannelPointsMiner/classes/entities/Bet.py delete mode 100644 TwitchChannelPointsMiner/classes/entities/EventPrediction.py diff --git a/README.md b/README.md index 2bd2b0c..662c944 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,6 @@ Read more about Twitch Drops [here](https://help.twitch.tv/s/article/mission-bas 5. 🔧 [Settings](#settings) - [LoggerSettings](#loggersettings) - [StreamerSettings](#streamersettings) - - [BetSettings](#betsettings) - - [Bet strategy](#bet-strategy) - - [FilterCondition](#filtercondition) - - [Example](#example) 6. 🍪 [Migrating from an old repository (the original one)](#migrating-from-an-old-repository-the-original-one) 7. 🪟 [Windows](#windows) 8. 📱 [Termux](#termux) @@ -58,7 +54,6 @@ If you appreciate the original work this project is based on, consider supportin - Automatic downloading of the list of followers and using it as an input ✔️ - Better 'Watch Streak' strategy in the priority system [#11](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/11) ✔️ - Auto claiming [game drops](https://help.twitch.tv/s/article/mission-based-drops) from the Twitch inventory [#21](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/21) ✔️ -- Placing a bet / making a prediction with your channel points [#41](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/41) ([@lay295](https://github.com/lay295)) ✔️ - Switchable analytics chart that shows the progress of your points with various annotations [#96](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/96) ✔️ - Joining the IRC Chat to increase the watch time and get StreamElements points [#47](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/47) ✔️ - [Moments](https://help.twitch.tv/s/article/moments) claiming [#182](https://github.com/rdavydov/Twitch-Channel-Points-Miner-v2/issues/182) ✔️ @@ -74,18 +69,9 @@ If you appreciate the original work this project is based on, consider supportin %d/%m/%y %H:%M:%S - INFO - [set_offline]: 😴 Streamer(username=streamer-username3, channel_id=0000000, channel_points=61365) is Offline! %d/%m/%y %H:%M:%S - INFO - [set_offline]: 😴 Streamer(username=streamer-username4, channel_id=0000000, channel_points=3760) is Offline! %d/%m/%y %H:%M:%S - INFO - [set_online]: 🥳 Streamer(username=streamer-username, channel_id=0000000, channel_points=61365) is Online! -%d/%m/%y %H:%M:%S - INFO - [start_bet]: 🔧 Start betting for EventPrediction(event_id=xxxx-xxxx-xxxx-xxxx, title=Please star this repo) owned by Streamer(username=streamer-username, channel_id=0000000, channel_points=61365) -%d/%m/%y %H:%M:%S - INFO - [__open_coins_menu]: 🔧 Open coins menu for EventPrediction(event_id=xxxx-xxxx-xxxx-xxxx, title=Please star this repo) -%d/%m/%y %H:%M:%S - INFO - [__click_on_bet]: 🔧 Click on the bet for EventPrediction(event_id=xxxx-xxxx-xxxx-xxxx, title=Please star this repo) -%d/%m/%y %H:%M:%S - INFO - [__enable_custom_bet_value]: 🔧 Enable input of custom value for EventPrediction(event_id=xxxx-xxxx-xxxx-xxxx, title=Please star this repo) -%d/%m/%y %H:%M:%S - INFO - [on_message]: ⏰ Place the bet after: 89.99s for: EventPrediction(event_id=xxxx-xxxx-xxxx-xxxx-15c61914ef69, title=Please star this repo) %d/%m/%y %H:%M:%S - INFO - [on_message]: 🚀 +12 → Streamer(username=streamer-username, channel_id=0000000, channel_points=61377) - Reason: WATCH. -%d/%m/%y %H:%M:%S - INFO - [make_predictions]: 🍀 Going to complete bet for EventPrediction(event_id=xxxx-xxxx-xxxx-xxxx-15c61914ef69, title=Please star this repo) owned by Streamer(username=streamer-username, channel_id=0000000, channel_points=61377) -%d/%m/%y %H:%M:%S - INFO - [make_predictions]: 🍀 Place 5k channel points on: SI (BLUE), Points: 848k, Users: 190 (70.63%), Odds: 1.24 (80.65%) -%d/%m/%y %H:%M:%S - INFO - [on_message]: 🚀 +6675 → Streamer(username=streamer-username, channel_id=0000000, channel_points=64206) - Reason: PREDICTION. -%d/%m/%y %H:%M:%S - INFO - [on_message]: 📊 EventPrediction(event_id=xxxx-xxxx-xxxx-xxxx, title=Please star this repo) - Result: WIN, Points won: 6675 -%d/%m/%y %H:%M:%S - INFO - [on_message]: 🚀 +12 → Streamer(username=streamer-username, channel_id=0000000, channel_points=64218) - Reason: WATCH. -%d/%m/%y %H:%M:%S - INFO - [on_message]: 🚀 +12 → Streamer(username=streamer-username, channel_id=0000000, channel_points=64230) - Reason: WATCH. +%d/%m/%y %H:%M:%S - INFO - [on_message]: 🚀 +12 → Streamer(username=streamer-username, channel_id=0000000, channel_points=61389) - Reason: WATCH. +%d/%m/%y %H:%M:%S - INFO - [on_message]: 🚀 +12 → Streamer(username=streamer-username, channel_id=0000000, channel_points=61401) - Reason: WATCH. %d/%m/%y %H:%M:%S - INFO - [claim_bonus]: 🎁 Claiming the bonus for Streamer(username=streamer-username, channel_id=0000000, channel_points=64230)! %d/%m/%y %H:%M:%S - INFO - [on_message]: 🚀 +60 → Streamer(username=streamer-username, channel_id=0000000, channel_points=64290) - Reason: CLAIM. %d/%m/%y %H:%M:%S - INFO - [on_message]: 🚀 +12 → Streamer(username=streamer-username, channel_id=0000000, channel_points=64326) - Reason: WATCH. @@ -105,17 +91,7 @@ If you appreciate the original work this project is based on, consider supportin %d/%m %H:%M:%S - 😴 streamer-username3 (xxx points) is Offline! %d/%m %H:%M:%S - 😴 streamer-username4 (xxx points) is Offline! %d/%m %H:%M:%S - 🥳 streamer-username (xxx points) is Online! -%d/%m %H:%M:%S - 🔧 Start betting for EventPrediction: Please star this repo owned by streamer-username (xxx points) -%d/%m %H:%M:%S - 🔧 Open coins menu for EventPrediction: Please star this repo -%d/%m %H:%M:%S - 🔧 Click on the bet for EventPrediction: Please star this repo -%d/%m %H:%M:%S - 🔧 Enable input of custom value for EventPrediction: Please star this repo -%d/%m %H:%M:%S - ⏰ Place the bet after: 89.99s EventPrediction: Please star this repo -%d/%m %H:%M:%S - 🚀 +12 → streamer-username (xxx points) - Reason: WATCH. -%d/%m %H:%M:%S - 🍀 Going to complete bet for EventPrediction: Please star this repo owned by streamer-username (xxx points) -%d/%m %H:%M:%S - 🍀 Place 5k channel points on: SI (BLUE), Points: 848k, Users: 190 (70.63%), Odds: 1.24 (80.65%) -%d/%m %H:%M:%S - 🚀 +6675 → streamer-username (xxx points) - Reason: PREDICTION. -%d/%m %H:%M:%S - 📊 EventPrediction: Please star this repo - Result: WIN, Points won: 6675 -%d/%m %H:%M:%S - 🚀 +12 → streamer-username (xxx points) - Reason: WATCH. +%d/%m %H:%M:%S - +12 → streamer-username (xxx points) - Reason: WATCH. %d/%m %H:%M:%S - 🚀 +12 → streamer-username (xxx points) - Reason: WATCH. %d/%m %H:%M:%S - 🚀 +60 → streamer-username (xxx points) - Reason: CLAIM. %d/%m %H:%M:%S - 🚀 +12 → streamer-username (xxx points) - Reason: WATCH. @@ -131,28 +107,8 @@ If you appreciate the original work this project is based on, consider supportin %d/%m/%y %H:%M:%S - 📄 Logs file: /.../path/Twitch-Channel-Points-Miner-v2/logs/username.timestamp.log %d/%m/%y %H:%M:%S - ⌛ Duration 10:29:19.547371 -%d/%m/%y %H:%M:%S - 📊 BetSettings(Strategy=Strategy.SMART, Percentage=7, PercentageGap=20, MaxPoints=7500 -%d/%m/%y %H:%M:%S - 📊 EventPrediction(event_id=xxxx-xxxx-xxxx-xxxx, title="Event Title1") - Streamer(username=streamer-username, channel_id=0000000, channel_points=67247) - Bet(TotalUsers=1k, TotalPoints=11M), Decision={'choice': 'B', 'amount': 5289, 'id': 'xxxx-yyyy-zzzz'}) - Outcome0(YES (BLUE) Points: 7M, Users: 641 (58.49%), Odds: 1.6, (5}%) - Outcome1(NO (PINK),Points: 4M, Users: 455 (41.51%), Odds: 2.65 (37.74%)) - Result: {'type': 'LOSE', 'won': 0} -%d/%m/%y %H:%M:%S - 📊 EventPrediction(event_id=yyyy-yyyy-yyyy-yyyy, title="Event Title2") - Streamer(username=streamer-username, channel_id=0000000, channel_points=3453464) - Bet(TotalUsers=921, TotalPoints=11M), Decision={'choice': 'A', 'amount': 4926, 'id': 'xxxx-yyyy-zzzz'}) - Outcome0(YES (BLUE) Points: 9M, Users: 562 (61.02%), Odds: 1.31 (76.34%)) - Outcome1(YES (PINK) Points: 3M, Users: 359 (38.98%), Odds: 4.21 (23.75%)) - Result: {'type': 'WIN', 'won': 6531} -%d/%m/%y %H:%M:%S - 📊 EventPrediction(event_id=ad152117-251b-4666-b683-18e5390e56c3, title="Event Title3") - Streamer(username=streamer-username, channel_id=0000000, channel_points=45645645) - Bet(TotalUsers=260, TotalPoints=3M), Decision={'choice': 'A', 'amount': 5054, 'id': 'xxxx-yyyy-zzzz'}) - Outcome0(YES (BLUE) Points: 689k, Users: 114 (43.85%), Odds: 4.24 (23.58%)) - Outcome1(NO (PINK) Points: 2M, Users: 146 (56.15%), Odds: 1.31 (76.34%)) - Result: {'type': 'LOSE', 'won': 0} - -%d/%m/%y %H:%M:%S - 🤖 Streamer(username=streamer-username, channel_id=0000000, channel_points=67247), Total points gained (after farming - before farming): -7838 -%d/%m/%y %H:%M:%S - 💰 CLAIM(11 times, 550 gained), PREDICTION(1 times, 6531 gained), WATCH(35 times, 350 gained) +%d/%m/%y %H:%M:%S - 🤖 Streamer(username=streamer-username, channel_id=0000000, channel_points=67247), Total points gained (after farming - before farming): 1243 +%d/%m/%y %H:%M:%S - 💰 CLAIM(11 times, 550 gained), WATCH(35 times, 350 gained) %d/%m/%y %H:%M:%S - 🤖 Streamer(username=streamer-username2, channel_id=0000000, channel_points=61365), Total points gained (after farming - before farming): 977 %d/%m/%y %H:%M:%S - 💰 CLAIM(4 times, 240 gained), REFUND(1 times, 605 gained), WATCH(11 times, 132 gained) %d/%m/%y %H:%M:%S - 🤖 Streamer(username=streamer-username5, channel_id=0000000, channel_points=25960), Total points gained (after farming - before farming): 1680 @@ -176,7 +132,6 @@ from TwitchChannelPointsMiner.classes.Webhook import Webhook from TwitchChannelPointsMiner.classes.Telegram import Telegram from TwitchChannelPointsMiner.classes.Gotify import Gotify from TwitchChannelPointsMiner.classes.Settings import Priority, Events, FollowersOrder -from TwitchChannelPointsMiner.classes.entities.Bet import Strategy, BetSettings, Condition, OutcomeKeys, FilterCondition, DelayMode from TwitchChannelPointsMiner.classes.entities.Streamer import Streamer, StreamerSettings twitch_miner = TwitchChannelPointsMiner( @@ -208,28 +163,28 @@ twitch_miner = TwitchChannelPointsMiner( ), telegram=Telegram( # You can omit or set to None if you don't want to receive updates on Telegram chat_id=123456789, # Chat ID to send messages @getmyid_bot - token="123456789:shfuihreuifheuifhiu34578347", # Telegram API token @BotFather + token="123456789:shfuihreuifheuifheuifhiu34578347", # Telegram API token @BotFather events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], # Only these events will be sent to the chat + Events.CHAT_MENTION], # Only these events will be sent to the chat disable_notification=True, # Revoke the notification (sound/vibration) ), discord=Discord( webhook_api="https://discord.com/api/webhooks/0123456789/0a1B2c3D4e5F6g7H8i9J", # Discord Webhook URL events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], # Only these events will be sent to the chat + Events.CHAT_MENTION], # Only these events will be sent to the chat ), webhook=Webhook( endpoint="https://example.com/webhook", # Webhook URL method="GET", # GET or POST events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], # Only these events will be sent to the endpoint + Events.CHAT_MENTION], # Only these events will be sent to the endpoint ), matrix=Matrix( username="twitch_miner", # Matrix username (without homeserver) password="...", # Matrix password homeserver="matrix.org", # Matrix homeserver room_id="...", # Room ID - events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, Events.BET_LOSE], # Only these events will be sent + events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE], # Only these events will be sent ), pushover=Pushover( userkey="YOUR-ACCOUNT-TOKEN", # Login to https://pushover.net/, the user token is on the main page @@ -242,32 +197,16 @@ twitch_miner = TwitchChannelPointsMiner( endpoint="https://example.com/message?token=TOKEN", priority=8, events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], + Events.CHAT_MENTION], ) ), 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 claim_moments=True, # If set to True, https://help.twitch.tv/s/article/moments will be claimed when available watch_streak=True, # If a streamer go online change the priority of streamers array and catch the watch screak. Issue #11 community_goals=False, # If True, contributes the max channel points per stream to the streamers' community challenge goals chat=ChatPresence.ONLINE, # Join irc chat to increase watch-time [ALWAYS, NEVER, ONLINE, OFFLINE] - 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 strategy) - max_points=50000, # If the x percentage of your channel points is gt bet_max_points set this value - stealth_mode=True, # If the calculated amount of channel points is GT the highest bet, place the highest value minus 1-2 points Issue #33 - delay_mode=DelayMode.FROM_END, # When placing a bet, we will wait until `delay` seconds before the end of the timer - delay=6, - minimum_points=20000, # Place the bet only if we have at least 20k points. Issue #113 - filter_condition=FilterCondition( - by=OutcomeKeys.TOTAL_USERS, # Where apply the filter. Allowed [PERCENTAGE_USERS, ODDS_PERCENTAGE, ODDS, TOP_POINTS, TOTAL_USERS, TOTAL_POINTS] - where=Condition.LTE, # 'by' must be [GT, LT, GTE, LTE] than value - value=800 - ) - ) ) ) @@ -276,18 +215,18 @@ twitch_miner = TwitchChannelPointsMiner( # 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. +# For example, if in the mine function you don't provide any value 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.analytics(host="127.0.0.1", port=5000, refresh=5, days_ago=7) # Start the Analytics web-server (replit: host="0.0.0.0") twitch_miner.mine( [ - Streamer("streamer-username01", settings=StreamerSettings(make_predictions=True , follow_raid=False , claim_drops=True , watch_streak=True , community_goals=False , bet=BetSettings(strategy=Strategy.SMART , percentage=5 , stealth_mode=True, percentage_gap=20 , max_points=234 , filter_condition=FilterCondition(by=OutcomeKeys.TOTAL_USERS, where=Condition.LTE, value=800 ) ) )), - Streamer("streamer-username02", settings=StreamerSettings(make_predictions=False , follow_raid=True , claim_drops=False , bet=BetSettings(strategy=Strategy.PERCENTAGE , percentage=5 , stealth_mode=False, percentage_gap=20 , max_points=1234 , filter_condition=FilterCondition(by=OutcomeKeys.TOTAL_POINTS, where=Condition.GTE, value=250 ) ) )), - Streamer("streamer-username03", settings=StreamerSettings(make_predictions=True , follow_raid=False , watch_streak=True , community_goals=True , bet=BetSettings(strategy=Strategy.SMART , percentage=5 , stealth_mode=False, percentage_gap=30 , max_points=50000 , filter_condition=FilterCondition(by=OutcomeKeys.ODDS, where=Condition.LT, value=300 ) ) )), - 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 , community_goals=True , bet=BetSettings(strategy=Strategy.HIGH_ODDS , percentage=7 , stealth_mode=True, percentage_gap=20 , max_points=90 , filter_condition=FilterCondition(by=OutcomeKeys.PERCENTAGE_USERS, where=Condition.GTE, value=300 ) ) )), + Streamer("streamer-username01", settings=StreamerSettings(follow_raid=False , claim_drops=True , watch_streak=True , community_goals=False )), + Streamer("streamer-username02", settings=StreamerSettings(follow_raid=True , claim_drops=False )), + Streamer("streamer-username03", settings=StreamerSettings(follow_raid=False , watch_streak=True , community_goals=True )), + Streamer("streamer-username04", settings=StreamerSettings(follow_raid=True , watch_streak=True )), + Streamer("streamer-username05", settings=StreamerSettings(follow_raid=True , claim_drops=True , watch_streak=True , community_goals=True )), Streamer("streamer-username06"), Streamer("streamer-username07"), Streamer("streamer-username08"), @@ -442,12 +381,6 @@ ColorPalette( GAIN_FOR_CLAIM = Fore.YELLOW, GAIN_FOR_WATCH = Fore.YELLOW, GAIN_FOR_WATCH_STREAK = 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, ) ``` @@ -469,7 +402,7 @@ Telegram( chat_id=123456789, token="123456789:shfuihreuifheuifhiu34578347", events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], + Events.CHAT_MENTION], disable_notification=True, ) ``` @@ -494,7 +427,7 @@ If you want to receive log updates on Discord initialize a new Discord class, el Discord( webhook_api="https://discord.com/api/webhooks/0123456789/0a1B2c3D4e5F6g7H8i9J", events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], + Events.CHAT_MENTION], ) ``` @@ -512,7 +445,7 @@ Webhook( endpoint="https://example.com/webhook", method="GET", events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], + Events.CHAT_MENTION], ) ``` @@ -523,13 +456,6 @@ Webhook( - `GAIN_FOR_RAID` - `GAIN_FOR_CLAIM` - `GAIN_FOR_WATCH` - - `BET_WIN` - - `BET_LOSE` - - `BET_REFUND` - - `BET_FILTERS` - - `BET_GENERAL` - - `BET_FAILED` - - `BET_START` - `BONUS_CLAIM` - `MOMENT_CLAIM` - `JOIN_RAID` @@ -540,13 +466,11 @@ Webhook( ### 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 | | `claim_drops` | bool | True | If this value is True, the script will increase the watch-time for the current game. With this, you can claim the drops from Twitch Inventory [#21](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/21) | | `claim_moments` | bool | True | If set to True, [moments](https://help.twitch.tv/s/article/moments) will be claimed when available | | `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) | | `community_goals` | bool | False | If True, contributes the max channel points per stream to the streamers' community challenge goals | -| `bet` | BetSettings | | Rules to follow for the bet | | `chat` | ChatPresence | ONLINE | Join IRC-Chat to appear online in chat and attempt to get StreamElements channel points and increase view-time [#47](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/47) | Allowed values for `chat` are: @@ -555,82 +479,6 @@ Allowed values for `chat` are: - `ONLINE` Partecipate to IRC chat if the streamer is online (leave if offline) - `OFFLINE` Partecipate to IRC chat if the streamer is offline (leave if online) -### BetSettings -| Key | Type | Default | Description | -|-------------------- |----------------- |--------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `strategy` | Strategy | SMART | Choose your strategy! See below for more info | -| `percentage` | int | 5 | Place the x% of your channel points | -| `percentage_gap` | int | 20 | Gap difference between outcomesA and outcomesB (for SMART stragegy) | -| `max_points` | int | 50000 | If the x percentage of your channel points is GT bet_max_points set this value | -| `stealth_mode` | bool | False | If the calculated amount of channel points is GT the highest bet, place the highest value minus 1-2 points [#33](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/33) | -| `delay_mode` | DelayMode | FROM_END | Define how is calculating the waiting time before placing a bet | -| `delay` | float | 6 | Value to be used to calculate bet delay depending on `delay_mode` value | - -#### Bet strategy - -- **MOST_VOTED**: Select the option most voted based on users count -- **HIGH_ODDS**: Select the option with the highest odds -- **PERCENTAGE**: Select the option with the highest percentage based on odds (It's the same that show Twitch) - Should be the same as select LOWEST_ODDS -- **SMART_MONEY**: Select the option with the highest points placed. [#331](https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues/331) -- **SMART**: If the majority in percent chose an option, then follow the other users, otherwise select the option with the highest odds -- **NUMBER_1**: Always select the 1st option, BLUE side if there are only two options -- **NUMBER_2**: Always select the 2nd option, PINK side if there are only two options -- **NUMBER_3**: Always select the 3rd option -- **NUMBER_4**: Always select the 4th option -- **NUMBER_5**: Always select the 5th option -- **NUMBER_6**: Always select the 6th option -- **NUMBER_7**: Always select the 7th option -- **NUMBER_8**: Always select the 8th option - -![Screenshot](https://raw.githubusercontent.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/master/assets/prediction.png) - -Here a concrete example: - -- **MOST_VOTED**: 21 Users have select **'over 7.5'**, instead of 9 'under 7.5' -- **HIGH_ODDS**: The highest odd is 2.27 on **'over 7.5'** vs 1.79 on 'under 7.5' -- **PERCENTAGE**: The highest percentage is 56% for **'under 7.5'** -- **SMART**: Calculate the percentage based on the users. The percentages are: 'over 7.5': 70% and 'under 7.5': 30%. If the difference between the two percentages is higher than `percentage_gap` select the highest percentage, else the highest odds. - -In this case if percentage_gap = 20 ; 70-30 = 40 > percentage_gap, so the bot will select 'over 7.5' -### FilterCondition -| Key | Type | Default | Description | -|------------- |------------- |--------- |---------------------------------------------------------------------------------- | -| `by` | OutcomeKeys | None | Key to apply the filter | -| `where` | Condition | None | Condition that should match for place bet | -| `value` | number | None | Value to compare | - -Allowed values for `by` are: -- `PERCENTAGE_USERS` (no sum) [Would never want a sum as it'd always be 100%] -- `ODDS_PERCENTAGE` (no sum) [Doesn't make sense to sum odds] -- `ODDS` (no sum) [Doesn't make sense to sum odds] -- `DECISION_USERS` (no sum) -- `DECISION_POINTS` (no sum) -- `TOP_POINTS` (no sum) [Doesn't make sense to the top points of both sides] -- `TOTAL_USERS` (sum) -- `TOTAL_POINTS` (sum) - -Allowed values for `where` are: `GT, LT, GTE, LTE` - -#### Example -- If you want to place the bet ONLY if the total of users participants in the bet is greater than 200 -`FilterCondition(by=OutcomeKeys.TOTAL_USERS, where=Condition.GT, value=200)` -- If you want to place the bet ONLY if the winning odd of your decision is greater than or equal to 1.3 -`FilterCondition(by=OutcomeKeys.ODDS, where=Condition.GTE, value=1.3)` -- If you want to place the bet ONLY if the highest bet is lower than 2000 -`FilterCondition(by=OutcomeKeys.TOP_POINTS, where=Condition.LT, value=2000)` - -### DelayMode - -- **FROM_START**: Will wait `delay` seconds from when the bet was opened -- **FROM_END**: Will until there is `delay` seconds left to place the bet -- **PERCENTAGE**: Will place the bet when `delay` percent of the set timer is elapsed - -Here's a concrete example. Let's suppose we have a bet that is opened with a timer of 10 minutes: - -- **FROM_START** with `delay=20`: The bet will be placed 20s after the bet is opened -- **FROM_END** with `delay=20`: The bet will be placed 20s before the end of the bet (so 9mins 40s after the bet is opened) -- **PERCENTAGE** with `delay=0.2`: The bet will be placed when the timer went down by 20% (so 2mins after the bet is opened) - ## Migrating from an old repository (the original one): If you already have a `twitch-cookies.pkl` and you don't want to log in again, please create a `cookies/` folder in the current directory and then copy the .pkl file with a new name `your-twitch-username.pkl` ``` diff --git a/TwitchChannelPointsMiner/TwitchChannelPointsMiner.py b/TwitchChannelPointsMiner/TwitchChannelPointsMiner.py index de2d301..c533cbe 100644 --- a/TwitchChannelPointsMiner/TwitchChannelPointsMiner.py +++ b/TwitchChannelPointsMiner/TwitchChannelPointsMiner.py @@ -59,7 +59,6 @@ class TwitchChannelPointsMiner: "disable_at_in_nickname", "priority", "streamers", - "events_predictions", "minute_watcher_thread", "sync_campaigns_thread", "ws_pool", @@ -132,7 +131,6 @@ class TwitchChannelPointsMiner: # Init as default all the missing values streamer_settings.default() - streamer_settings.bet.default() Settings.streamer_settings = streamer_settings # user_agent = get_user_agent("FIREFOX") @@ -143,7 +141,6 @@ class TwitchChannelPointsMiner: self.priority = priority if isinstance(priority, list) else [priority] self.streamers: list[Streamer] = [] - self.events_predictions = {} self.minute_watcher_thread = None self.sync_campaigns_thread = None self.ws_pool = None @@ -272,9 +269,6 @@ class TwitchChannelPointsMiner: streamer.settings = set_default_settings( streamer.settings, Settings.streamer_settings ) - streamer.settings.bet = set_default_settings( - streamer.settings.bet, Settings.streamer_settings.bet - ) if streamer.settings.chat != ChatPresence.NEVER: streamer.irc_chat = ThreadChat( self.username, @@ -291,13 +285,11 @@ class TwitchChannelPointsMiner: # Populate the streamers with default values. # 1. Load channel points and auto-claim bonus # 2. Check if streamers are online - # 3. DEACTIVATED: Check if the user is a moderator. (was used before the 5th of April 2021 to deactivate predictions) for streamer in self.streamers: time.sleep(random.uniform(0.3, 0.7)) try: self.twitch.load_channel_points_context(streamer) self.twitch.check_streamer_online(streamer) - # self.twitch.viewer_is_mod(streamer) except StreamerDoesNotExistException: logger.info( f"Streamer {streamer.username} does not exist", @@ -308,11 +300,6 @@ class TwitchChannelPointsMiner: streamer.channel_points for streamer in self.streamers ] - # 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 - ) - # If we have at least one streamer with settings = claim_drops True # Spawn a thread for sync inventory and dashboard if ( @@ -337,7 +324,6 @@ class TwitchChannelPointsMiner: self.ws_pool = WebSocketsPool( twitch=self.twitch, streamers=self.streamers, - events_predictions=self.events_predictions, ) # Subscribe to community-points-user. Get update for points spent or gains @@ -356,15 +342,6 @@ class TwitchChannelPointsMiner: ) ) - # 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=user_id, - ) - ) - for streamer in self.streamers: self.ws_pool.submit( PubsubTopic("video-playback-by-id", streamer=streamer) @@ -373,11 +350,6 @@ class TwitchChannelPointsMiner: if streamer.settings.follow_raid is True: self.ws_pool.submit(PubsubTopic("raid", streamer=streamer)) - if streamer.settings.make_predictions is True: - self.ws_pool.submit( - PubsubTopic("predictions-channel-v1", streamer=streamer) - ) - if streamer.settings.claim_moments is True: self.ws_pool.submit( PubsubTopic("community-moments-channel-v1", streamer=streamer) @@ -465,28 +437,6 @@ class TwitchChannelPointsMiner: extra={"emoji": ":hourglass:"}, ) - if not Settings.logger.less and self.events_predictions != {}: - print("") - for event_id in self.events_predictions: - event = self.events_predictions[event_id] - if ( - event.bet_confirmed is True - and event.streamer.settings.make_predictions is True - ): - logger.info( - f"{event.streamer.settings.bet}", - extra={"emoji": ":wrench:"}, - ) - if event.streamer.settings.bet.filter_condition is not None: - logger.info( - f"{event.streamer.settings.bet.filter_condition}", - extra={"emoji": ":pushpin:"}, - ) - logger.info( - f"{event.print_recap()}", - extra={"emoji": ":bar_chart:"}, - ) - print("") for streamer_index in range(0, len(self.streamers)): if self.streamers[streamer_index].history != {}: diff --git a/TwitchChannelPointsMiner/classes/Settings.py b/TwitchChannelPointsMiner/classes/Settings.py index 3f0c381..d896026 100644 --- a/TwitchChannelPointsMiner/classes/Settings.py +++ b/TwitchChannelPointsMiner/classes/Settings.py @@ -31,13 +31,6 @@ class Events(Enum): GAIN_FOR_CLAIM = auto() GAIN_FOR_WATCH = auto() GAIN_FOR_WATCH_STREAK = auto() - BET_WIN = auto() - BET_LOSE = auto() - BET_REFUND = auto() - BET_FILTERS = auto() - BET_GENERAL = auto() - BET_FAILED = auto() - BET_START = auto() BONUS_CLAIM = auto() MOMENT_CLAIM = auto() JOIN_RAID = auto() diff --git a/TwitchChannelPointsMiner/classes/Twitch.py b/TwitchChannelPointsMiner/classes/Twitch.py index 411c338..95ac395 100644 --- a/TwitchChannelPointsMiner/classes/Twitch.py +++ b/TwitchChannelPointsMiner/classes/Twitch.py @@ -693,89 +693,6 @@ class Twitch(object): if streamer.settings.community_goals is True: self.contribute_to_community_goals(streamer) - if streamer.settings.community_goals is True: - self.contribute_to_community_goals(streamer) - - def make_predictions(self, event): - decision = event.bet.calculate(event.streamer.channel_points) - # selector_index = 0 if decision["choice"] == "A" else 1 - - logger.info( - f"Going to complete bet for {event}", - extra={ - "emoji": ":four_leaf_clover:", - "event": Events.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:", - "event": Events.BET_FILTERS, - }, - ) - logger.info( - f"Skip settings {event.bet.settings.filter_condition}, current value is: {compared_value}", - extra={ - "emoji": ":pushpin:", - "event": Events.BET_FILTERS, - }, - ) - else: - if decision["amount"] >= 10: - logger.info( - # f"Place {_millify(decision['amount'])} channel points on: {event.bet.get_outcome(selector_index)}", - f"Place {_millify(decision['amount'])} channel points on: {event.bet.get_outcome(decision['choice'])}", - extra={ - "emoji": ":four_leaf_clover:", - "event": Events.BET_GENERAL, - }, - ) - - json_data = copy.deepcopy(GQLOperations.MakePrediction) - json_data["variables"] = { - "input": { - "eventID": event.event_id, - "outcomeID": decision["id"], - "points": decision["amount"], - "transactionID": token_hex(16), - } - } - response = self.post_gql_request(json_data) - if ( - "data" in response - and "makePrediction" in response["data"] - and "error" in response["data"]["makePrediction"] - and response["data"]["makePrediction"]["error"] is not None - ): - error_code = response["data"]["makePrediction"]["error"]["code"] - logger.error( - f"Failed to place bet, error: {error_code}", - extra={ - "emoji": ":four_leaf_clover:", - "event": Events.BET_FAILED, - }, - ) - else: - logger.info( - f"Bet won't be placed as the amount {_millify(decision['amount'])} is less than the minimum required 10", - extra={ - "emoji": ":four_leaf_clover:", - "event": Events.BET_GENERAL, - }, - ) - else: - logger.info( - f"Oh no! The event is not active anymore! Current status: {event.status}", - extra={ - "emoji": ":disappointed_relieved:", - "event": Events.BET_FAILED, - }, - ) - def claim_bonus(self, streamer, claim_id): if Settings.logger.less is False: logger.info( diff --git a/TwitchChannelPointsMiner/classes/WebSocketsPool.py b/TwitchChannelPointsMiner/classes/WebSocketsPool.py index 372597e..b3019ef 100644 --- a/TwitchChannelPointsMiner/classes/WebSocketsPool.py +++ b/TwitchChannelPointsMiner/classes/WebSocketsPool.py @@ -9,7 +9,6 @@ from threading import Thread, Timer from dateutil import parser from TwitchChannelPointsMiner.classes.entities.CommunityGoal import CommunityGoal -from TwitchChannelPointsMiner.classes.entities.EventPrediction import EventPrediction from TwitchChannelPointsMiner.classes.entities.Message import Message from TwitchChannelPointsMiner.classes.entities.Raid import Raid from TwitchChannelPointsMiner.classes.Settings import Events, Settings @@ -24,13 +23,12 @@ logger = logging.getLogger(__name__) class WebSocketsPool: - __slots__ = ["ws", "twitch", "streamers", "events_predictions"] + __slots__ = ["ws", "twitch", "streamers"] - def __init__(self, twitch, streamers, events_predictions): + def __init__(self, twitch, streamers): self.ws = [] self.twitch = twitch self.streamers = streamers - self.events_predictions = events_predictions """ API Limits @@ -254,152 +252,6 @@ class WebSocketsPool: ws.streamers[streamer_index], message.data["moment_id"] ) - elif message.topic == "predictions-channel-v1": - - event_dict = message.data["event"] - event_id = event_dict["id"] - event_status = event_dict["status"] - - current_tmsp = parser.parse(message.timestamp) - - if ( - message.type == "event-created" - and event_id not in ws.events_predictions - ): - if event_status == "ACTIVE": - prediction_window_seconds = float( - event_dict["prediction_window_seconds"] - ) - # Reduce prediction window by 3/6s - Collect more accurate data for decision - prediction_window_seconds = ws.streamers[ - streamer_index - ].get_prediction_window(prediction_window_seconds) - event = EventPrediction( - ws.streamers[streamer_index], - event_id, - event_dict["title"], - parser.parse(event_dict["created_at"]), - prediction_window_seconds, - event_status, - event_dict["outcomes"], - ) - if ( - ws.streamers[streamer_index].is_online - and event.closing_bet_after(current_tmsp) > 0 - ): - streamer = ws.streamers[streamer_index] - bet_settings = streamer.settings.bet - if ( - bet_settings.minimum_points is None - or streamer.channel_points - > bet_settings.minimum_points - ): - ws.events_predictions[event_id] = event - start_after = event.closing_bet_after( - current_tmsp - ) - - place_bet_thread = Timer( - start_after, - ws.twitch.make_predictions, - (ws.events_predictions[event_id],), - ) - place_bet_thread.daemon = True - place_bet_thread.start() - - logger.info( - f"Place the bet after: {start_after}s for: {ws.events_predictions[event_id]}", - extra={ - "emoji": ":alarm_clock:", - "event": Events.BET_START, - }, - ) - else: - logger.info( - f"{streamer} have only {streamer.channel_points} channel points and the minimum for bet is: {bet_settings.minimum_points}", - extra={ - "emoji": ":pushpin:", - "event": Events.BET_FILTERS, - }, - ) - - elif ( - message.type == "event-updated" - and event_id in ws.events_predictions - ): - ws.events_predictions[event_id].status = event_status - # Game over we can't update anymore the values... The bet was placed! - if ( - ws.events_predictions[event_id].bet_placed is False - and ws.events_predictions[event_id].bet.decision == {} - ): - ws.events_predictions[event_id].bet.update_outcomes( - event_dict["outcomes"] - ) - - elif message.topic == "predictions-user-v1": - event_id = message.data["prediction"]["event_id"] - if event_id in ws.events_predictions: - event_prediction = ws.events_predictions[event_id] - if ( - message.type == "prediction-result" - and event_prediction.bet_confirmed - ): - points = event_prediction.parse_result( - message.data["prediction"]["result"] - ) - - decision = event_prediction.bet.get_decision() - choice = event_prediction.bet.decision["choice"] - - logger.info( - ( - f"{event_prediction} - Decision: {choice}: {decision['title']} " - f"({decision['color']}) - Result: {event_prediction.result['string']}" - ), - extra={ - "emoji": ":bar_chart:", - "event": Events.get( - f"BET_{event_prediction.result['type']}" - ), - }, - ) - - ws.streamers[streamer_index].update_history( - "PREDICTION", points["gained"] - ) - - # Remove duplicate history records from previous message sent in community-points-user-v1 - if event_prediction.result["type"] == "REFUND": - ws.streamers[streamer_index].update_history( - "REFUND", - -points["placed"], - counter=-1, - ) - elif event_prediction.result["type"] == "WIN": - ws.streamers[streamer_index].update_history( - "PREDICTION", - -points["won"], - counter=-1, - ) - - if event_prediction.result["type"]: - # Analytics switch - if Settings.enable_analytics is True: - ws.streamers[ - streamer_index - ].persistent_annotations( - event_prediction.result["type"], - f"{ws.events_predictions[event_id].title}", - ) - elif message.type == "prediction-made": - event_prediction.bet_confirmed = True - # Analytics switch - if Settings.enable_analytics is True: - ws.streamers[streamer_index].persistent_annotations( - "PREDICTION_MADE", - f"Decision: {event_prediction.bet.decision['choice']} - {event_prediction.title}", - ) elif message.topic == "community-points-channel-v1": if message.type == "community-goal-created": # TODO Untested, hard to find this happening live diff --git a/TwitchChannelPointsMiner/classes/entities/Bet.py b/TwitchChannelPointsMiner/classes/entities/Bet.py deleted file mode 100644 index 9d65b78..0000000 --- a/TwitchChannelPointsMiner/classes/entities/Bet.py +++ /dev/null @@ -1,345 +0,0 @@ -import copy -from enum import Enum, auto -from random import uniform - -from millify import millify - -#from TwitchChannelPointsMiner.utils import char_decision_as_index, float_round -from TwitchChannelPointsMiner.utils import float_round - - -class Strategy(Enum): - MOST_VOTED = auto() - HIGH_ODDS = auto() - PERCENTAGE = auto() - SMART_MONEY = auto() - SMART = auto() - NUMBER_1 = auto() - NUMBER_2 = auto() - NUMBER_3 = auto() - NUMBER_4 = auto() - NUMBER_5 = auto() - NUMBER_6 = auto() - NUMBER_7 = auto() - NUMBER_8 = auto() - - def __str__(self): - return self.name - - -class Condition(Enum): - GT = auto() - LT = auto() - GTE = auto() - LTE = auto() - - def __str__(self): - return self.name - - -class OutcomeKeys(object): - # Real key on Bet dict [''] - PERCENTAGE_USERS = "percentage_users" - ODDS_PERCENTAGE = "odds_percentage" - ODDS = "odds" - TOP_POINTS = "top_points" - # Real key on Bet dict [''] - Sum() - TOTAL_USERS = "total_users" - TOTAL_POINTS = "total_points" - # This key does not exist - DECISION_USERS = "decision_users" - DECISION_POINTS = "decision_points" - - -class DelayMode(Enum): - FROM_START = auto() - FROM_END = auto() - PERCENTAGE = auto() - - def __str__(self): - return self.name - - -class FilterCondition(object): - __slots__ = [ - "by", - "where", - "value", - ] - - def __init__(self, by=None, where=None, value=None, decision=None): - self.by = by - self.where = where - self.value = value - - def __repr__(self): - return f"FilterCondition(by={self.by.upper()}, where={self.where}, value={self.value})" - - -class BetSettings(object): - __slots__ = [ - "strategy", - "percentage", - "percentage_gap", - "max_points", - "minimum_points", - "stealth_mode", - "filter_condition", - "delay", - "delay_mode", - ] - - def __init__( - self, - strategy: Strategy = None, - percentage: int = None, - percentage_gap: int = None, - max_points: int = None, - minimum_points: int = None, - stealth_mode: bool = None, - filter_condition: FilterCondition = None, - delay: float = None, - delay_mode: DelayMode = None, - ): - self.strategy = strategy - self.percentage = percentage - self.percentage_gap = percentage_gap - self.max_points = max_points - self.minimum_points = minimum_points - self.stealth_mode = stealth_mode - self.filter_condition = filter_condition - self.delay = delay - self.delay_mode = delay_mode - - def default(self): - self.strategy = self.strategy if self.strategy is not None else Strategy.SMART - self.percentage = self.percentage if self.percentage is not None else 5 - self.percentage_gap = ( - self.percentage_gap if self.percentage_gap is not None else 20 - ) - self.max_points = self.max_points if self.max_points is not None else 50000 - self.minimum_points = ( - self.minimum_points if self.minimum_points is not None else 0 - ) - self.stealth_mode = ( - self.stealth_mode if self.stealth_mode is not None else False - ) - self.delay = self.delay if self.delay is not None else 6 - self.delay_mode = ( - self.delay_mode if self.delay_mode is not None else DelayMode.FROM_END - ) - - def __repr__(self): - return f"BetSettings(strategy={self.strategy}, percentage={self.percentage}, percentage_gap={self.percentage_gap}, max_points={self.max_points}, minimum_points={self.minimum_points}, stealth_mode={self.stealth_mode})" - - -class Bet(object): - __slots__ = ["outcomes", "decision", "total_users", "total_points", "settings"] - - def __init__(self, outcomes: list, settings: BetSettings): - self.outcomes = outcomes - self.__clear_outcomes() - self.decision: dict = {} - self.total_users = 0 - self.total_points = 0 - self.settings = settings - - def update_outcomes(self, outcomes): - for index in range(0, len(self.outcomes)): - self.outcomes[index][OutcomeKeys.TOTAL_USERS] = int( - outcomes[index][OutcomeKeys.TOTAL_USERS] - ) - self.outcomes[index][OutcomeKeys.TOTAL_POINTS] = int( - outcomes[index][OutcomeKeys.TOTAL_POINTS] - ) - if outcomes[index]["top_predictors"] != []: - # Sort by points placed by other users - outcomes[index]["top_predictors"] = sorted( - outcomes[index]["top_predictors"], - key=lambda x: x["points"], - reverse=True, - ) - # Get the first elements (most placed) - top_points = outcomes[index]["top_predictors"][0]["points"] - self.outcomes[index][OutcomeKeys.TOP_POINTS] = top_points - - # Inefficient, but otherwise outcomekeys are represented wrong - self.total_points = 0 - self.total_users = 0 - for index in range(0, len(self.outcomes)): - self.total_users += self.outcomes[index][OutcomeKeys.TOTAL_USERS] - self.total_points += self.outcomes[index][OutcomeKeys.TOTAL_POINTS] - - if ( - self.total_users > 0 - and self.total_points > 0 - ): - for index in range(0, len(self.outcomes)): - self.outcomes[index][OutcomeKeys.PERCENTAGE_USERS] = float_round( - (100 * self.outcomes[index][OutcomeKeys.TOTAL_USERS]) / self.total_users - ) - self.outcomes[index][OutcomeKeys.ODDS] = float_round( - #self.total_points / max(self.outcomes[index][OutcomeKeys.TOTAL_POINTS], 1) - 0 - if self.outcomes[index][OutcomeKeys.TOTAL_POINTS] == 0 - else self.total_points / self.outcomes[index][OutcomeKeys.TOTAL_POINTS] - ) - self.outcomes[index][OutcomeKeys.ODDS_PERCENTAGE] = float_round( - #100 / max(self.outcomes[index][OutcomeKeys.ODDS], 1) - 0 - if self.outcomes[index][OutcomeKeys.ODDS] == 0 - else 100 / self.outcomes[index][OutcomeKeys.ODDS] - ) - - self.__clear_outcomes() - - def __repr__(self): - return f"Bet(total_users={millify(self.total_users)}, total_points={millify(self.total_points)}), decision={self.decision})\n\t\tOutcome A({self.get_outcome(0)})\n\t\tOutcome B({self.get_outcome(1)})" - - def get_decision(self, parsed=False): - #decision = self.outcomes[0 if self.decision["choice"] == "A" else 1] - decision = self.outcomes[self.decision["choice"]] - return decision if parsed is False else Bet.__parse_outcome(decision) - - @staticmethod - def __parse_outcome(outcome): - return f"{outcome['title']} ({outcome['color']}), Points: {millify(outcome[OutcomeKeys.TOTAL_POINTS])}, Users: {millify(outcome[OutcomeKeys.TOTAL_USERS])} ({outcome[OutcomeKeys.PERCENTAGE_USERS]}%), Odds: {outcome[OutcomeKeys.ODDS]} ({outcome[OutcomeKeys.ODDS_PERCENTAGE]}%)" - - def get_outcome(self, index): - return Bet.__parse_outcome(self.outcomes[index]) - - def __clear_outcomes(self): - for index in range(0, len(self.outcomes)): - keys = copy.deepcopy(list(self.outcomes[index].keys())) - for key in keys: - if key not in [ - OutcomeKeys.TOTAL_USERS, - OutcomeKeys.TOTAL_POINTS, - OutcomeKeys.TOP_POINTS, - OutcomeKeys.PERCENTAGE_USERS, - OutcomeKeys.ODDS, - OutcomeKeys.ODDS_PERCENTAGE, - "title", - "color", - "id", - ]: - del self.outcomes[index][key] - for key in [ - OutcomeKeys.PERCENTAGE_USERS, - OutcomeKeys.ODDS, - OutcomeKeys.ODDS_PERCENTAGE, - OutcomeKeys.TOP_POINTS, - ]: - if key not in self.outcomes[index]: - self.outcomes[index][key] = 0 - - '''def __return_choice(self, key) -> str: - return "A" if self.outcomes[0][key] > self.outcomes[1][key] else "B"''' - - def __return_choice(self, key) -> int: - largest=0 - for index in range(0, len(self.outcomes)): - if self.outcomes[index][key] > self.outcomes[largest][key]: - largest = index - return largest - - def __return_number_choice(self, number) -> int: - if (len(self.outcomes) > number): - return number - else: - return 0 - - def skip(self) -> bool: - if self.settings.filter_condition is not None: - # key == by , condition == where - key = self.settings.filter_condition.by - condition = self.settings.filter_condition.where - value = self.settings.filter_condition.value - - fixed_key = ( - key - if key not in [OutcomeKeys.DECISION_USERS, OutcomeKeys.DECISION_POINTS] - else key.replace("decision", "total") - ) - if key in [OutcomeKeys.TOTAL_USERS, OutcomeKeys.TOTAL_POINTS]: - compared_value = ( - self.outcomes[0][fixed_key] + self.outcomes[1][fixed_key] - ) - else: - #outcome_index = char_decision_as_index(self.decision["choice"]) - outcome_index = self.decision["choice"] - compared_value = self.outcomes[outcome_index][fixed_key] - - # Check if condition is satisfied - if condition == Condition.GT: - if compared_value > value: - return False, compared_value - elif condition == Condition.LT: - if compared_value < value: - return False, compared_value - elif condition == Condition.GTE: - if compared_value >= value: - return False, compared_value - elif condition == Condition.LTE: - if compared_value <= value: - return False, compared_value - return True, compared_value # Else skip the bet - else: - return False, 0 # Default don't skip the bet - - def calculate(self, balance: int) -> dict: - self.decision = {"choice": None, "amount": 0, "id": None} - if self.settings.strategy == Strategy.MOST_VOTED: - self.decision["choice"] = self.__return_choice(OutcomeKeys.TOTAL_USERS) - elif self.settings.strategy == Strategy.HIGH_ODDS: - self.decision["choice"] = self.__return_choice(OutcomeKeys.ODDS) - elif self.settings.strategy == Strategy.PERCENTAGE: - self.decision["choice"] = self.__return_choice(OutcomeKeys.ODDS_PERCENTAGE) - elif self.settings.strategy == Strategy.SMART_MONEY: - self.decision["choice"] = self.__return_choice(OutcomeKeys.TOP_POINTS) - elif self.settings.strategy == Strategy.NUMBER_1: - self.decision["choice"] = self.__return_number_choice(0) - elif self.settings.strategy == Strategy.NUMBER_2: - self.decision["choice"] = self.__return_number_choice(1) - elif self.settings.strategy == Strategy.NUMBER_3: - self.decision["choice"] = self.__return_number_choice(2) - elif self.settings.strategy == Strategy.NUMBER_4: - self.decision["choice"] = self.__return_number_choice(3) - elif self.settings.strategy == Strategy.NUMBER_5: - self.decision["choice"] = self.__return_number_choice(4) - elif self.settings.strategy == Strategy.NUMBER_6: - self.decision["choice"] = self.__return_number_choice(5) - elif self.settings.strategy == Strategy.NUMBER_7: - self.decision["choice"] = self.__return_number_choice(6) - elif self.settings.strategy == Strategy.NUMBER_8: - self.decision["choice"] = self.__return_number_choice(7) - elif self.settings.strategy == Strategy.SMART: - difference = abs( - self.outcomes[0][OutcomeKeys.PERCENTAGE_USERS] - - self.outcomes[1][OutcomeKeys.PERCENTAGE_USERS] - ) - self.decision["choice"] = ( - self.__return_choice(OutcomeKeys.ODDS) - if difference < self.settings.percentage_gap - else self.__return_choice(OutcomeKeys.TOTAL_USERS) - ) - - if self.decision["choice"] is not None: - #index = char_decision_as_index(self.decision["choice"]) - index = self.decision["choice"] - self.decision["id"] = self.outcomes[index]["id"] - self.decision["amount"] = min( - int(balance * (self.settings.percentage / 100)), - self.settings.max_points, - ) - if ( - self.settings.stealth_mode is True - and self.decision["amount"] - >= self.outcomes[index][OutcomeKeys.TOP_POINTS] - ): - reduce_amount = uniform(1, 5) - self.decision["amount"] = ( - self.outcomes[index][OutcomeKeys.TOP_POINTS] - reduce_amount - ) - self.decision["amount"] = int(self.decision["amount"]) - return self.decision diff --git a/TwitchChannelPointsMiner/classes/entities/EventPrediction.py b/TwitchChannelPointsMiner/classes/entities/EventPrediction.py deleted file mode 100644 index 58dc761..0000000 --- a/TwitchChannelPointsMiner/classes/entities/EventPrediction.py +++ /dev/null @@ -1,94 +0,0 @@ -from TwitchChannelPointsMiner.classes.entities.Bet import Bet -from TwitchChannelPointsMiner.classes.entities.Streamer import Streamer -from TwitchChannelPointsMiner.classes.Settings import Settings -from TwitchChannelPointsMiner.utils import _millify, float_round - - -class EventPrediction(object): - __slots__ = [ - "streamer", - "event_id", - "title", - "created_at", - "prediction_window_seconds", - "status", - "result", - "box_fillable", - "bet_confirmed", - "bet_placed", - "bet", - ] - - def __init__( - self, - streamer: Streamer, - event_id, - title, - created_at, - prediction_window_seconds, - status, - outcomes, - ): - self.streamer = streamer - - self.event_id = event_id - self.title = title.strip() - self.created_at = created_at - self.prediction_window_seconds = prediction_window_seconds - self.status = status - self.result: dict = {"string": "", "type": None, "gained": 0} - - self.box_fillable = False - self.bet_confirmed = False - self.bet_placed = False - self.bet = Bet(outcomes, streamer.settings.bet) - - def __repr__(self): - return f"EventPrediction(event_id={self.event_id}, streamer={self.streamer}, title={self.title})" - - def __str__(self): - return ( - f"EventPrediction: {self.streamer} - {self.title}" - if Settings.logger.less - else self.__repr__() - ) - - def elapsed(self, timestamp): - return float_round((timestamp - self.created_at).total_seconds()) - - def closing_bet_after(self, timestamp): - return float_round(self.prediction_window_seconds - self.elapsed(timestamp)) - - def print_recap(self) -> str: - return f"{self}\n\t\t{self.bet}\n\t\tResult: {self.result['string']}" - - def parse_result(self, result) -> dict: - result_type = result["type"] - - points = {} - points["placed"] = ( - self.bet.decision["amount"] if result_type != "REFUND" else 0 - ) - points["won"] = ( - result["points_won"] - if result["points_won"] or result_type == "REFUND" - else 0 - ) - points["gained"] = ( - points["won"] - points["placed"] if result_type != "REFUND" else 0 - ) - points["prefix"] = "+" if points["gained"] >= 0 else "" - - action = ( - "Lost" - if result_type == "LOSE" - else ("Refunded" if result_type == "REFUND" else "Gained") - ) - - self.result = { - "string": f"{result_type}, {action}: {points['prefix']}{_millify(points['gained'])}", - "type": result_type, - "gained": points["gained"], - } - - return points diff --git a/TwitchChannelPointsMiner/classes/entities/Streamer.py b/TwitchChannelPointsMiner/classes/entities/Streamer.py index 23ada3d..9beb017 100644 --- a/TwitchChannelPointsMiner/classes/entities/Streamer.py +++ b/TwitchChannelPointsMiner/classes/entities/Streamer.py @@ -6,7 +6,6 @@ from datetime import datetime from threading import Lock from TwitchChannelPointsMiner.classes.Chat import ChatPresence, ThreadChat -from TwitchChannelPointsMiner.classes.entities.Bet import BetSettings, DelayMode from TwitchChannelPointsMiner.classes.entities.Stream import Stream from TwitchChannelPointsMiner.classes.Settings import Events, Settings from TwitchChannelPointsMiner.constants import URL @@ -17,39 +16,32 @@ logger = logging.getLogger(__name__) class StreamerSettings(object): __slots__ = [ - "make_predictions", "follow_raid", "claim_drops", "claim_moments", "watch_streak", "community_goals", - "bet", "chat", ] def __init__( self, - make_predictions: bool = None, follow_raid: bool = None, claim_drops: bool = None, claim_moments: bool = None, watch_streak: bool = None, community_goals: bool = None, - bet: BetSettings = None, chat: ChatPresence = None, ): - self.make_predictions = make_predictions self.follow_raid = follow_raid self.claim_drops = claim_drops self.claim_moments = claim_moments self.watch_streak = watch_streak self.community_goals = community_goals - self.bet = bet self.chat = chat def default(self): for name in [ - "make_predictions", "follow_raid", "claim_drops", "claim_moments", @@ -59,13 +51,11 @@ class StreamerSettings(object): setattr(self, name, True) if self.community_goals is None: self.community_goals = False - if self.bet is None: - self.bet = BetSettings() if self.chat is None: self.chat = ChatPresence.ONLINE def __repr__(self): - return f"BetSettings(make_predictions={self.make_predictions}, follow_raid={self.follow_raid}, claim_drops={self.claim_drops}, claim_moments={self.claim_moments}, watch_streak={self.watch_streak}, community_goals={self.community_goals}, bet={self.bet}, chat={self.chat})" + return f"StreamerSettings(follow_raid={self.follow_raid}, claim_drops={self.claim_drops}, claim_moments={self.claim_moments}, watch_streak={self.watch_streak}, community_goals={self.community_goals}, chat={self.chat})" class Streamer(object): @@ -199,18 +189,6 @@ class Streamer(object): else 0 ) - def get_prediction_window(self, prediction_window_seconds): - delay_mode = self.settings.bet.delay_mode - delay = self.settings.bet.delay - if delay_mode == DelayMode.FROM_START: - return min(delay, prediction_window_seconds) - elif delay_mode == DelayMode.FROM_END: - return max(prediction_window_seconds - delay, 0) - elif delay_mode == DelayMode.PERCENTAGE: - return prediction_window_seconds * delay - else: - return prediction_window_seconds - # === ANALYTICS === # def persistent_annotations(self, event_type, event_text): event_type = event_type.upper() diff --git a/example.py b/example.py index 17060ba..fa5ed3d 100644 --- a/example.py +++ b/example.py @@ -12,7 +12,6 @@ from TwitchChannelPointsMiner.classes.Matrix import Matrix from TwitchChannelPointsMiner.classes.Pushover import Pushover from TwitchChannelPointsMiner.classes.Gotify import Gotify from TwitchChannelPointsMiner.classes.Settings import Priority, Events, FollowersOrder -from TwitchChannelPointsMiner.classes.entities.Bet import Strategy, BetSettings, Condition, OutcomeKeys, FilterCondition, DelayMode from TwitchChannelPointsMiner.classes.entities.Streamer import Streamer, StreamerSettings twitch_miner = TwitchChannelPointsMiner( @@ -40,32 +39,31 @@ twitch_miner = TwitchChannelPointsMiner( 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 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]. ), telegram=Telegram( # You can omit or set to None if you don't want to receive updates on Telegram chat_id=123456789, # Chat ID to send messages @getmyid_bot token="123456789:shfuihreuifheuifhiu34578347", # Telegram API token @BotFather events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], # Only these events will be sent to the chat + Events.CHAT_MENTION], # Only these events will be sent to the chat disable_notification=True, # Revoke the notification (sound/vibration) ), discord=Discord( webhook_api="https://discord.com/api/webhooks/0123456789/0a1B2c3D4e5F6g7H8i9J", # Discord Webhook URL events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], # Only these events will be sent to the chat + Events.CHAT_MENTION], # Only these events will be sent to the chat ), webhook=Webhook( endpoint="https://example.com/webhook", # Webhook URL method="GET", # GET or POST events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], # Only these events will be sent to the endpoint + Events.CHAT_MENTION], # Only these events will be sent to the endpoint ), matrix=Matrix( username="twitch_miner", # Matrix username (without homeserver) password="...", # Matrix password homeserver="matrix.org", # Matrix homeserver room_id="...", # Room ID - events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, Events.BET_LOSE], # Only these events will be sent + events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE], # Only these events will be sent ), pushover=Pushover( userkey="YOUR-ACCOUNT-TOKEN", # Login to https://pushover.net/, the user token is on the main page @@ -78,32 +76,16 @@ twitch_miner = TwitchChannelPointsMiner( endpoint="https://example.com/message?token=TOKEN", priority=8, events=[Events.STREAMER_ONLINE, Events.STREAMER_OFFLINE, - Events.BET_LOSE, Events.CHAT_MENTION], + Events.CHAT_MENTION], ) ), 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 claim_moments=True, # If set to True, https://help.twitch.tv/s/article/moments will be claimed when available watch_streak=True, # If a streamer go online change the priority of streamers array and catch the watch screak. Issue #11 community_goals=False, # If True, contributes the max channel points per stream to the streamers' community challenge goals chat=ChatPresence.ONLINE, # Join irc chat to increase watch-time [ALWAYS, NEVER, ONLINE, OFFLINE] - 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 strategy) - max_points=50000, # If the x percentage of your channel points is gt bet_max_points set this value - stealth_mode=True, # If the calculated amount of channel points is GT the highest bet, place the highest value minus 1-2 points Issue #33 - delay_mode=DelayMode.FROM_END, # When placing a bet, we will wait until `delay` seconds before the end of the timer - delay=6, - minimum_points=20000, # Place the bet only if we have at least 20k points. Issue #113 - filter_condition=FilterCondition( - by=OutcomeKeys.TOTAL_USERS, # Where apply the filter. Allowed [PERCENTAGE_USERS, ODDS_PERCENTAGE, ODDS, TOP_POINTS, TOTAL_USERS, TOTAL_POINTS] - where=Condition.LTE, # 'by' must be [GT, LT, GTE, LTE] than value - value=800 - ) - ) ) ) @@ -119,11 +101,11 @@ twitch_miner = TwitchChannelPointsMiner( twitch_miner.mine( [ - Streamer("streamer-username01", settings=StreamerSettings(make_predictions=True , follow_raid=False , claim_drops=True , watch_streak=True , community_goals=False , bet=BetSettings(strategy=Strategy.SMART , percentage=5 , stealth_mode=True, percentage_gap=20 , max_points=234 , filter_condition=FilterCondition(by=OutcomeKeys.TOTAL_USERS, where=Condition.LTE, value=800 ) ) )), - Streamer("streamer-username02", settings=StreamerSettings(make_predictions=False , follow_raid=True , claim_drops=False , bet=BetSettings(strategy=Strategy.PERCENTAGE , percentage=5 , stealth_mode=False, percentage_gap=20 , max_points=1234 , filter_condition=FilterCondition(by=OutcomeKeys.TOTAL_POINTS, where=Condition.GTE, value=250 ) ) )), - Streamer("streamer-username03", settings=StreamerSettings(make_predictions=True , follow_raid=False , watch_streak=True , community_goals=True , bet=BetSettings(strategy=Strategy.SMART , percentage=5 , stealth_mode=False, percentage_gap=30 , max_points=50000 , filter_condition=FilterCondition(by=OutcomeKeys.ODDS, where=Condition.LT, value=300 ) ) )), - 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 , community_goals=True , bet=BetSettings(strategy=Strategy.HIGH_ODDS , percentage=7 , stealth_mode=True, percentage_gap=20 , max_points=90 , filter_condition=FilterCondition(by=OutcomeKeys.PERCENTAGE_USERS, where=Condition.GTE, value=300 ) ) )), + Streamer("streamer-username01", settings=StreamerSettings(follow_raid=False , claim_drops=True , watch_streak=True , community_goals=False)), + Streamer("streamer-username02", settings=StreamerSettings(follow_raid=True , claim_drops=False)), + Streamer("streamer-username03", settings=StreamerSettings(follow_raid=False , watch_streak=True , community_goals=True)), + Streamer("streamer-username04", settings=StreamerSettings(follow_raid=True , watch_streak=True)), + Streamer("streamer-username05", settings=StreamerSettings(follow_raid=True , claim_drops=True , watch_streak=True , community_goals=True)), Streamer("streamer-username06"), Streamer("streamer-username07"), Streamer("streamer-username08"),