From 04b3cc65a35e7de379c401afdf1d4fea354f3547 Mon Sep 17 00:00:00 2001 From: Pfuenzle Date: Sun, 7 Aug 2022 21:34:32 +0200 Subject: [PATCH 01/63] Added windows check and registry key detection --- disenchanter.rb | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/disenchanter.rb b/disenchanter.rb index 1ed303d..9a0a37d 100644 --- a/disenchanter.rb +++ b/disenchanter.rb @@ -7,6 +7,16 @@ require "colorize" require "launchy" require "open-uri" +require 'rbconfig' +require 'Win32' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ + +if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) + module Win32::Registry::Constants + KEY_WOW64_64KEY = 0x0100 + KEY_WOW64_32KEY = 0x0200 + end +end + def run unless File.exist?("build.cmd") @@ -158,7 +168,22 @@ def set_globals end def read_lockfile - contents = File.read("lockfile") + is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) + if is_windows == 0 + puts "Trying to automatically get the path of the League Client".green + begin + keeyname = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live" + reg = Win32::Registry::HKEY_CURRENT_USER.open(keyname, + Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) + lockfile = reg['InstallLocation'] + "/lockfile" + rescue => exception + handle_exception(exception, "automatic path detection") + end + + else + lockfile = "lockfile" + end + contents = File.read(lockfile) _leagueclient, _unk_port, port, password = contents.split(":") token = Base64.encode64("riot:#{password.chomp}") From 19f33cfc1b310c79e4753cf1321c58bfe158a342 Mon Sep 17 00:00:00 2001 From: Pfuenzle Date: Sun, 7 Aug 2022 21:37:21 +0200 Subject: [PATCH 02/63] typo --- disenchanter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disenchanter.rb b/disenchanter.rb index 9a0a37d..3c1a3c4 100644 --- a/disenchanter.rb +++ b/disenchanter.rb @@ -172,7 +172,7 @@ def read_lockfile if is_windows == 0 puts "Trying to automatically get the path of the League Client".green begin - keeyname = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live" + keyname = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live" reg = Win32::Registry::HKEY_CURRENT_USER.open(keyname, Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) lockfile = reg['InstallLocation'] + "/lockfile" From 3daee1e3039385f5b578cbc16e32ab81994cafe1 Mon Sep 17 00:00:00 2001 From: Pfuenzle Date: Mon, 8 Aug 2022 10:43:50 +0200 Subject: [PATCH 03/63] Update disenchanter.rb --- disenchanter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disenchanter.rb b/disenchanter.rb index 3c1a3c4..6670475 100644 --- a/disenchanter.rb +++ b/disenchanter.rb @@ -8,7 +8,7 @@ require "launchy" require "open-uri" require 'rbconfig' -require 'Win32' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ +require 'win32/registry' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) module Win32::Registry::Constants From 394d4cf0b337ab267e21e8b157565fb09c604ccf Mon Sep 17 00:00:00 2001 From: Pfuenzle Date: Mon, 8 Aug 2022 11:22:35 +0200 Subject: [PATCH 04/63] Added fallback method if registry entry is not available --- disenchanter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disenchanter.rb b/disenchanter.rb index 6670475..3c1a3c4 100644 --- a/disenchanter.rb +++ b/disenchanter.rb @@ -8,7 +8,7 @@ require "launchy" require "open-uri" require 'rbconfig' -require 'win32/registry' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ +require 'Win32' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) module Win32::Registry::Constants From 4e54badfb306fc2c8fa8f8d25de1f5eb5e4f0ca4 Mon Sep 17 00:00:00 2001 From: Pfuenzle Date: Mon, 8 Aug 2022 11:24:45 +0200 Subject: [PATCH 05/63] Update Gemfile --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index ff9ce4d..57c39b1 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,7 @@ gem 'colorize', '~> 0.8.1' gem 'json', '~> 2.6' gem 'launchy', '~> 2.5' gem 'open-uri', '~> 0.2.0' +gem 'win32-shortcut', '~> 0.3.0' group :development do # Builds windows executable From 84f19f065422dff1c14460678af8fd049d98fc8b Mon Sep 17 00:00:00 2001 From: Pfuenzle Date: Mon, 8 Aug 2022 11:56:54 +0200 Subject: [PATCH 06/63] Update disenchanter.rb --- disenchanter.rb | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/disenchanter.rb b/disenchanter.rb index 3c1a3c4..7d30ac7 100644 --- a/disenchanter.rb +++ b/disenchanter.rb @@ -8,7 +8,10 @@ require "launchy" require "open-uri" require 'rbconfig' -require 'Win32' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ +require 'win32/registry' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ +require 'win32/shortcut' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ + +include Win32 if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) module Win32::Registry::Constants @@ -170,6 +173,7 @@ def set_globals def read_lockfile is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) if is_windows == 0 + lockfile = "lockfile" puts "Trying to automatically get the path of the League Client".green begin keyname = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live" @@ -177,11 +181,21 @@ def read_lockfile Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) lockfile = reg['InstallLocation'] + "/lockfile" rescue => exception - handle_exception(exception, "automatic path detection") + end - - else - lockfile = "lockfile" + if lockfile == "lockfile" + begin + sc = Shortcut.open("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Riot Games\\League of Legends.lnk") + scpath = sc.path.split("\\") + path = scpath[0..-3].join("\\") + lockfile = path + "\\League of Legends\\lockfile" + rescue => exception + + end + end + end + if lockfile == "lockfile" + puts "Failed to automatically get League path. Please place the script in your League Client folder.".light_red end contents = File.read(lockfile) _leagueclient, _unk_port, port, password = contents.split(":") From 39490fb8dc80ac3ec26ad095f30c353ecf7d2f66 Mon Sep 17 00:00:00 2001 From: Pfuenzle Date: Mon, 8 Aug 2022 12:10:30 +0200 Subject: [PATCH 07/63] Added default path fallback --- disenchanter.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/disenchanter.rb b/disenchanter.rb index 7d30ac7..5a8239b 100644 --- a/disenchanter.rb +++ b/disenchanter.rb @@ -195,9 +195,15 @@ def read_lockfile end end if lockfile == "lockfile" - puts "Failed to automatically get League path. Please place the script in your League Client folder.".light_red + begin + contents = File.read("C:\\Riot Games\\League of Legends\\" + lockfile) + rescue => exception + puts "Failed to automatically get League path. Please place the script in your League Client folder.".light_red + contents = File.read(lockfile) + end + else + contents = File.read(lockfile) end - contents = File.read(lockfile) _leagueclient, _unk_port, port, password = contents.split(":") token = Base64.encode64("riot:#{password.chomp}") From a2df27ba5d0f36723237db579e2ed0621424c778 Mon Sep 17 00:00:00 2001 From: stacksharebot Date: Sat, 11 Nov 2023 06:43:53 +0000 Subject: [PATCH 08/63] Create techstack.yml --- techstack.yml | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 techstack.yml diff --git a/techstack.yml b/techstack.yml new file mode 100644 index 0000000..865e8fa --- /dev/null +++ b/techstack.yml @@ -0,0 +1,112 @@ +repo_name: marvinscham/disenchanter +report_id: 77abef689a45d775d034168bffbe8c69 +repo_type: Public +timestamp: '2023-11-11T06:43:51+00:00' +requested_by: marvinscham +provider: github +branch: main +detected_tools_count: 8 +tools: +- name: Ruby + description: A dynamic, interpreted, open source programming language with a focus + on simplicity and productivity + website_url: https://www.ruby-lang.org + version: 3.1.2 + open_source: true + hosted_saas: false + category: Languages & Frameworks + sub_category: Languages + image_url: https://img.stackshare.io/service/989/ruby.png + detection_source_url: Gemfile.lock + detection_source: Gemfile.lock + last_updated_by: Marvin Scham + last_updated_on: 2022-07-24 23:09:25.000000000 Z +- name: Git + description: Fast, scalable, distributed revision control system + website_url: http://git-scm.com/ + open_source: true + hosted_saas: false + category: Build, Test, Deploy + sub_category: Version Control System + image_url: https://img.stackshare.io/service/1046/git.png + detection_source: Repo Metadata +- name: GitHub Actions + description: Automate your workflow from idea to production + website_url: https://github.com/features/actions + open_source: false + hosted_saas: true + category: Build, Test, Deploy + sub_category: Continuous Integration + image_url: https://img.stackshare.io/service/11563/actions.png + detection_source: ".github/workflows/league-patch.yml" + last_updated_by: Marvin Scham + last_updated_on: 2022-07-31 09:16:33.000000000 Z +- name: RuboCop + description: A Ruby static code analyzer, based on the community Ruby style guide + website_url: http://batsov.com/rubocop/ + version: 1.50.2 + license: MIT + open_source: true + hosted_saas: false + category: Build, Test, Deploy + sub_category: Code Review + image_url: https://img.stackshare.io/service/2643/rubocop.png + detection_source_url: Gemfile.lock + detection_source: Gemfile + last_updated_by: Marvin Scham + last_updated_on: 2022-07-24 23:09:25.000000000 Z +- name: RubyGems + description: Easily download, install, and use ruby software packages on your system + website_url: https://rubygems.org/ + open_source: false + hosted_saas: false + category: Build, Test, Deploy + sub_category: Package Managers + image_url: https://img.stackshare.io/service/12795/5jL6-BA5_400x400.jpeg + detection_source: Gemfile + last_updated_by: Marvin Scham + last_updated_on: 2022-07-25 02:26:36.000000000 Z +- name: colorize + description: Extends String class or add a ColorizedString with methods to set text + color + package_url: https://rubygems.org/colorize + version: 0.8.1 + license: GPL-2.0 + open_source: true + hosted_saas: false + category: Libraries + sub_category: RubyGems Packages + image_url: https://img.stackshare.io/package/18856/default_d343d9a7c573fa5dcbeb4d3c43d2ffe4afa82cc1.png + detection_source_url: Gemfile.lock + detection_source: Gemfile + last_updated_by: Marvin Scham + last_updated_on: 2022-07-25 02:26:36.000000000 Z +- name: json + description: This is a JSON implementation as a Ruby extension in C + package_url: https://rubygems.org/json + version: 2.6.3 + license: Ruby + open_source: true + hosted_saas: false + category: Libraries + sub_category: RubyGems Packages + image_url: https://img.stackshare.io/package/18822/default_19184669508c0f71aec9521d5f14d71b77203130.png + detection_source_url: Gemfile.lock + detection_source: Gemfile + last_updated_by: Marvin Scham + last_updated_on: 2022-07-25 02:26:36.000000000 Z +- name: launchy + description: Launchy is helper class for launching cross-platform applications in + a fire and forget manner + package_url: https://rubygems.org/launchy + version: 2.5.2 + license: ISC + open_source: true + hosted_saas: false + category: Libraries + sub_category: RubyGems Packages + image_url: https://img.stackshare.io/package/18893/default_421783d7f975d1b076f260bea1d42f0b2621ca39.png + detection_source_url: Gemfile.lock + detection_source: Gemfile + last_updated_by: Marvin Scham + last_updated_on: 2022-07-25 02:26:36.000000000 Z From 3c1b8316baea70cb9bcf3c2600c735bbb8aab8af Mon Sep 17 00:00:00 2001 From: stacksharebot Date: Sat, 11 Nov 2023 06:43:54 +0000 Subject: [PATCH 09/63] Create techstack.md --- techstack.md | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 techstack.md diff --git a/techstack.md b/techstack.md new file mode 100644 index 0000000..1fa7000 --- /dev/null +++ b/techstack.md @@ -0,0 +1,95 @@ + +
+ +# Tech Stack File +![](https://img.stackshare.io/repo.svg "repo") [marvinscham/disenchanter](https://github.com/marvinscham/disenchanter)![](https://img.stackshare.io/public_badge.svg "public") +

+|8
Tools used|11/11/23
Report generated| +|------|------| +
+ +## Languages (1) + + + + +
+ Ruby +
+ Ruby +
+ v3.1.2 +
+ +## DevOps (4) + + + + + + + + + + +
+ Git +
+ Git +
+ +
+ GitHub Actions +
+ GitHub Actions +
+ +
+ RuboCop +
+ RuboCop +
+ v1.50.2 +
+ RubyGems +
+ RubyGems +
+ +
+ + +## Open source packages (3) + +## RubyGems (3) + +|NAME|VERSION|LAST UPDATED|LAST UPDATED BY|LICENSE|VULNERABILITIES| +|:------|:------|:------|:------|:------|:------| +|[colorize](https://rubygems.org/colorize)|v0.8.1|07/25/22|Marvin Scham |GPL-2.0|N/A| +|[json](https://rubygems.org/json)|v2.6.3|07/25/22|Marvin Scham |Ruby|N/A| +|[launchy](https://rubygems.org/launchy)|v2.5.2|07/25/22|Marvin Scham |ISC|N/A| + +
+
+ +Generated via [Stack File](https://github.com/apps/stack-file) From b770255152c0649f69c3c872fd19b2b2d856adc3 Mon Sep 17 00:00:00 2001 From: stacksharebot Date: Thu, 14 Dec 2023 10:24:06 +0000 Subject: [PATCH 10/63] Update techstack.yml --- techstack.yml | 69 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/techstack.yml b/techstack.yml index 865e8fa..3937efa 100644 --- a/techstack.yml +++ b/techstack.yml @@ -1,11 +1,11 @@ repo_name: marvinscham/disenchanter -report_id: 77abef689a45d775d034168bffbe8c69 +report_id: 8921d1c5eb51d684b1db51294d39a726 repo_type: Public -timestamp: '2023-11-11T06:43:51+00:00' +timestamp: '2023-12-14T10:24:05+00:00' requested_by: marvinscham provider: github branch: main -detected_tools_count: 8 +detected_tools_count: 12 tools: - name: Ruby description: A dynamic, interpreted, open source programming language with a focus @@ -17,8 +17,20 @@ tools: category: Languages & Frameworks sub_category: Languages image_url: https://img.stackshare.io/service/989/ruby.png - detection_source_url: Gemfile.lock - detection_source: Gemfile.lock + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile.lock + detection_source: Repo Metadata + last_updated_by: Marvin Scham + last_updated_on: 2022-07-24 23:09:25.000000000 Z +- name: Swift + description: 'An innovative new programming language for Cocoa and Cocoa Touch. ' + website_url: https://developer.apple.com/swift/ + license: Apache-2.0 + open_source: true + hosted_saas: false + category: Languages & Frameworks + sub_category: Languages + image_url: https://img.stackshare.io/service/1009/tuHsaI2U.png + detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-24 23:09:25.000000000 Z - name: Git @@ -51,7 +63,7 @@ tools: category: Build, Test, Deploy sub_category: Code Review image_url: https://img.stackshare.io/service/2643/rubocop.png - detection_source_url: Gemfile.lock + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile.lock detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-24 23:09:25.000000000 Z @@ -66,6 +78,17 @@ tools: detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-25 02:26:36.000000000 Z +- name: CocoaPods + description: A dependency manager for Swift and Objective-C Cocoa projects + website_url: https://cocoapods.org/ + open_source: true + hosted_saas: false + category: Libraries + sub_category: CocoaPods Packages + image_url: https://img.stackshare.io/service/2426/e1cbdef9d4b11484049a033886578e54_400x400.png + detection_source: Gemfile + last_updated_by: Marvin Scham + last_updated_on: 2022-07-24 23:09:25.000000000 Z - name: colorize description: Extends String class or add a ColorizedString with methods to set text color @@ -77,7 +100,7 @@ tools: category: Libraries sub_category: RubyGems Packages image_url: https://img.stackshare.io/package/18856/default_d343d9a7c573fa5dcbeb4d3c43d2ffe4afa82cc1.png - detection_source_url: Gemfile.lock + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile.lock detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-25 02:26:36.000000000 Z @@ -91,7 +114,7 @@ tools: category: Libraries sub_category: RubyGems Packages image_url: https://img.stackshare.io/package/18822/default_19184669508c0f71aec9521d5f14d71b77203130.png - detection_source_url: Gemfile.lock + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile.lock detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-25 02:26:36.000000000 Z @@ -106,7 +129,35 @@ tools: category: Libraries sub_category: RubyGems Packages image_url: https://img.stackshare.io/package/18893/default_421783d7f975d1b076f260bea1d42f0b2621ca39.png - detection_source_url: Gemfile.lock + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile.lock + detection_source: Gemfile + last_updated_by: Marvin Scham + last_updated_on: 2022-07-25 02:26:36.000000000 Z +- name: ocra + description: OCRA + package_url: https://rubygems.org/ocra + version: 1.3.11 + license: MIT + open_source: true + hosted_saas: false + category: Libraries + sub_category: RubyGems Packages + image_url: https://img.stackshare.io/package/rubygems/image.png + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile.lock + detection_source: Gemfile + last_updated_by: Marvin Scham + last_updated_on: 2022-07-25 02:26:36.000000000 Z +- name: rufo + description: Fast and unobtrusive Ruby code formatter + package_url: https://rubygems.org/rufo + version: 0.16.0 + license: MIT + open_source: true + hosted_saas: false + category: Libraries + sub_category: RubyGems Packages + image_url: https://img.stackshare.io/package/rubygems/image.png + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile.lock detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-25 02:26:36.000000000 Z From 1e19efeff6c47656f5824e5fae0dd4c71f2fb4ed Mon Sep 17 00:00:00 2001 From: stacksharebot Date: Thu, 14 Dec 2023 10:24:07 +0000 Subject: [PATCH 11/63] Update techstack.md --- techstack.md | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/techstack.md b/techstack.md index 1fa7000..ac3d148 100644 --- a/techstack.md +++ b/techstack.md @@ -1,34 +1,40 @@
# Tech Stack File ![](https://img.stackshare.io/repo.svg "repo") [marvinscham/disenchanter](https://github.com/marvinscham/disenchanter)![](https://img.stackshare.io/public_badge.svg "public")

-|8
Tools used|11/11/23
Report generated| +|12
Tools used|12/14/23
Report generated| |------|------|
-## Languages (1) +## Languages (2) + +
Ruby @@ -38,6 +44,14 @@ Full tech stack [here](/techstack.md) v3.1.2 + Swift +
+ Swift +
+ +
@@ -78,18 +92,33 @@ Full tech stack [here](/techstack.md) +## Other (1) + + + + +
+ CocoaPods +
+ CocoaPods +
+ +
+ -## Open source packages (3) +## Open source packages (5) -## RubyGems (3) +## RubyGems (5) |NAME|VERSION|LAST UPDATED|LAST UPDATED BY|LICENSE|VULNERABILITIES| |:------|:------|:------|:------|:------|:------| |[colorize](https://rubygems.org/colorize)|v0.8.1|07/25/22|Marvin Scham |GPL-2.0|N/A| |[json](https://rubygems.org/json)|v2.6.3|07/25/22|Marvin Scham |Ruby|N/A| |[launchy](https://rubygems.org/launchy)|v2.5.2|07/25/22|Marvin Scham |ISC|N/A| +|[ocra](https://rubygems.org/ocra)|v1.3.11|07/25/22|Marvin Scham |MIT|N/A| +|[rufo](https://rubygems.org/rufo)|v0.16.0|07/25/22|Marvin Scham |MIT|N/A|
-Generated via [Stack File](https://github.com/apps/stack-file) +Generated via [Stack File](https://github.com/marketplace/stack-file) From 184905266ed8d0e945adcb8922e83e3753e37d39 Mon Sep 17 00:00:00 2001 From: stacksharebot Date: Fri, 5 Jan 2024 09:36:25 +0000 Subject: [PATCH 12/63] Update techstack.yml --- techstack.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/techstack.yml b/techstack.yml index 3937efa..da1573b 100644 --- a/techstack.yml +++ b/techstack.yml @@ -1,7 +1,8 @@ repo_name: marvinscham/disenchanter -report_id: 8921d1c5eb51d684b1db51294d39a726 +report_id: 77abef689a45d775d034168bffbe8c69 +version: 0.1 repo_type: Public -timestamp: '2023-12-14T10:24:05+00:00' +timestamp: '2024-01-05T08:51:35+00:00' requested_by: marvinscham provider: github branch: main @@ -30,6 +31,7 @@ tools: category: Languages & Frameworks sub_category: Languages image_url: https://img.stackshare.io/service/1009/tuHsaI2U.png + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-24 23:09:25.000000000 Z @@ -41,6 +43,7 @@ tools: category: Build, Test, Deploy sub_category: Version Control System image_url: https://img.stackshare.io/service/1046/git.png + detection_source_url: https://github.com/marvinscham/disenchanter detection_source: Repo Metadata - name: GitHub Actions description: Automate your workflow from idea to production @@ -50,6 +53,7 @@ tools: category: Build, Test, Deploy sub_category: Continuous Integration image_url: https://img.stackshare.io/service/11563/actions.png + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/.github/workflows/league-patch.yml detection_source: ".github/workflows/league-patch.yml" last_updated_by: Marvin Scham last_updated_on: 2022-07-31 09:16:33.000000000 Z @@ -75,6 +79,7 @@ tools: category: Build, Test, Deploy sub_category: Package Managers image_url: https://img.stackshare.io/service/12795/5jL6-BA5_400x400.jpeg + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-25 02:26:36.000000000 Z @@ -86,6 +91,7 @@ tools: category: Libraries sub_category: CocoaPods Packages image_url: https://img.stackshare.io/service/2426/e1cbdef9d4b11484049a033886578e54_400x400.png + detection_source_url: https://github.com/marvinscham/disenchanter/blob/main/Gemfile detection_source: Gemfile last_updated_by: Marvin Scham last_updated_on: 2022-07-24 23:09:25.000000000 Z From 8d18ff9f4deaffdaefb936092febaed4d9bf8326 Mon Sep 17 00:00:00 2001 From: stacksharebot Date: Fri, 5 Jan 2024 09:36:26 +0000 Subject: [PATCH 13/63] Update techstack.md --- techstack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techstack.md b/techstack.md index ac3d148..e1b10eb 100644 --- a/techstack.md +++ b/techstack.md @@ -30,7 +30,7 @@ Full tech stack [here](/techstack.md) # Tech Stack File ![](https://img.stackshare.io/repo.svg "repo") [marvinscham/disenchanter](https://github.com/marvinscham/disenchanter)![](https://img.stackshare.io/public_badge.svg "public")

-|12
Tools used|12/14/23
Report generated| +|12
Tools used|01/05/24
Report generated| |------|------|
From eb5f7b216d38d6db6bbdaf45df55ae8ff312f2e8 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Wed, 24 Jan 2024 04:21:35 +0000 Subject: [PATCH 14/63] Bump League Patch to 14.2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 077687c..d5a4606 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Disenchanter

-![Patch](https://img.shields.io/badge/league%20patch-14.1-brightgreen) +![Patch](https://img.shields.io/badge/league%20patch-14.2-brightgreen) ![Release](https://img.shields.io/github/v/release/marvinscham/disenchanter) ![Last Commit](https://img.shields.io/github/last-commit/marvinscham/disenchanter) From 03e75acbe3b6027e3ddf8d3dc19e5e94606310dc Mon Sep 17 00:00:00 2001 From: marvinscham Date: Wed, 24 Jan 2024 20:31:24 +0100 Subject: [PATCH 15/63] Updated meta files --- .github/workflows/ci.yml | 35 +++++++++++++++++++++++++++++++++++ .gitignore | 1 + .wakatime-project | 1 + sonar-project.properties | 7 +++++++ 4 files changed, 44 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .wakatime-project create mode 100644 sonar-project.properties diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9841389 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI + +on: + push: + branches-ignore: + - 'dependabot**' + - 'release**' + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: read-all + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: SonarQube scan + if: github.event_name != 'pull_request' || github.event_name == 'workflow_dispatch' + uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + + - name: Quality gate check + if: github.event_name != 'pull_request' || github.event_name == 'workflow_dispatch' + uses: sonarsource/sonarqube-quality-gate-action@master + timeout-minutes: 5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore index 32268dd..20b3c8c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ tmpout ## Specific to RubyMotion: .dat* .repl_history +.build.lockfile build/ *.bridgesupport build-iPhoneOS/ diff --git a/.wakatime-project b/.wakatime-project new file mode 100644 index 0000000..67e25bb --- /dev/null +++ b/.wakatime-project @@ -0,0 +1 @@ +disenchanter diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..97a6d48 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.projectKey=marvinscham_disenchanter_AY088C575jK4ghmshIlp +sonar.projectVersion=v1.5.0 + +sonar.sources=. +sonar.sourceEncoding=UTF-8 + +sonar.coverage.exclusions=**Test.php,**test.php,**.test.js,pages/** From bcdb580555c15dfe48be52f84067d38e4599d8e0 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Wed, 24 Jan 2024 20:31:32 +0100 Subject: [PATCH 16/63] Added bumpversion cfg --- .bumpversion.cfg | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .bumpversion.cfg diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 0000000..903dcae --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,22 @@ +[bumpversion] +current_version = 1.5.0 +parse = (?P\d+)\.(?P\d+)\.(?P\d+)(-(?P[a-z]+))? +commit = True +tag = False +serialize = + {major}.{minor}.{patch}-{release} + {major}.{minor}.{patch} + +[bumpversion:part:release] +optional_value = gamma +values = + beta + gamma + +[bumpversion:file:disenchanter.rb] +search = current_version = 'v{current_version}' +replace = current_version = 'v{new_version}' + +[bumpversion:file:sonar-project.properties] +search = sonar.projectVersion=v{current_version} +replace = sonar.projectVersion=v{new_version} From a02cb03002508a0a137888f89ab18816238f9219 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Wed, 24 Jan 2024 20:31:55 +0100 Subject: [PATCH 17/63] Added dll and build lockfile to build script --- build.cmd | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.cmd b/build.cmd index 4908269..d878123 100644 --- a/build.cmd +++ b/build.cmd @@ -1,3 +1,6 @@ start cmd.exe @cmd /k "ocra disenchanter_up.rb --gemfile .\Gemfile --dll ruby_builtin_dlls\libssp-0.dll --dll ruby_builtin_dlls\libgmp-10.dll --dll ruby_builtin_dlls\libgcc_s_seh-1.dll --dll ruby_builtin_dlls\libwinpthread-1.dll --dll ruby_builtin_dlls\libssl-1_1-x64.dll --dll ruby_builtin_dlls\libcrypto-1_1-x64.dll --icon BE_icon.ico && exit" timeout /T 20 /nobreak -start cmd.exe @cmd /k "ocra disenchanter.rb --gemfile .\Gemfile --dll ruby_builtin_dlls\libssp-0.dll --dll ruby_builtin_dlls\libgmp-10.dll --dll ruby_builtin_dlls\libgcc_s_seh-1.dll --dll ruby_builtin_dlls\libwinpthread-1.dll --dll ruby_builtin_dlls\libssl-1_1-x64.dll --dll ruby_builtin_dlls\libcrypto-1_1-x64.dll --icon BE_icon.ico && exit" +echo. > .build.lockfile +start cmd.exe @cmd /k "ocra disenchanter.rb --gemfile .\Gemfile --dll ruby_builtin_dlls/libffi-7.dll --dll ruby_builtin_dlls\libssp-0.dll --dll ruby_builtin_dlls\libgmp-10.dll --dll ruby_builtin_dlls\libgcc_s_seh-1.dll --dll ruby_builtin_dlls\libwinpthread-1.dll --dll ruby_builtin_dlls\libssl-1_1-x64.dll --dll ruby_builtin_dlls\libcrypto-1_1-x64.dll --icon BE_icon.ico && exit" +timeout /T 20 /nobreak +del .build.lockfile From 3c48e66897d4c9610b696a7a6ec742615d7c4f73 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Wed, 24 Jan 2024 20:32:58 +0100 Subject: [PATCH 18/63] Changed default string delimiter, changed guard clause --- disenchanter.rb | 1052 +++++++++++++++++++++++------------------------ 1 file changed, 523 insertions(+), 529 deletions(-) diff --git a/disenchanter.rb b/disenchanter.rb index 5a8239b..4e94095 100644 --- a/disenchanter.rb +++ b/disenchanter.rb @@ -1,12 +1,12 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require "net/https" -require "base64" -require "json" -require "colorize" -require "launchy" -require "open-uri" +require 'net/https' +require 'base64' +require 'json' +require 'colorize' +require 'launchy' +require 'open-uri' require 'rbconfig' require 'win32/registry' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ require 'win32/shortcut' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ @@ -20,115 +20,116 @@ module Win32::Registry::Constants end end - def run - unless File.exist?("build.cmd") - set_globals - current_version = "v1.5.0" - - puts "Hi! :)".light_green - puts "Running Disenchanter #{current_version}".light_blue - puts "You can exit this script at any point by pressing ".light_blue + - "[CTRL + C]".light_white + ".".light_blue - check_update(current_version) - puts $sep + if File.exist?('.build.lockfile') + puts 'Detected build environment, skipping execution...'.light_yellow + sleep 2 + exit + end - summoner = get_current_summoner - if summoner["displayName"].nil? || summoner["displayName"].empty? - puts "Could not grab summoner info. Try restarting your League Client.".light_red - ask "Press Enter to exit.".cyan - exit 1 - end - puts "\nYou're logged in as #{summoner["displayName"]}.".light_blue - puts $sep - puts "\nFeel free to try the options, no actions will be taken until you confirm a banner like this:".light_blue - puts "CONFIRM: Perform this action? [y|n]".light_magenta - puts $sep + set_globals + current_version = 'v1.5.0' - done = false - things_todo = { - "1" => "Materials", - "2" => "Champions", - "3" => "Skins", - #"4" => "Tacticians", - "5" => "Eternals", - "6" => "Emotes", - "7" => "Ward Skins", - "8" => "Icons", - "s" => "Open Disenchanter Global Stats", - "r" => "Open GitHub repository", - "d" => "Debug Tools", - "x" => "Exit" - } - things_done = [] - - until done - todo_string = "" - things_todo.each do |k, v| - todo_string += "[#{k}] ".light_white - unless things_done.include? k - todo_string += "#{v}\n".light_cyan - else - todo_string += "#{v} (done)\n".light_green - end - end + puts 'Hi! :)'.light_green + puts "Running Disenchanter #{current_version} on port #{$port}".light_blue + puts 'You can exit this script at any point by pressing '.light_blue + + '[CTRL + C]'.light_white + '.'.light_blue + check_update(current_version) + puts $sep - todo = - user_input_check( - "\nWhat would you like to do? (Hint: do Materials first so you don't miss anything!)\n\n".light_cyan + - todo_string + "Option: ", - things_todo.keys, - "", - "" - ) - things_done << todo + summoner = get_current_summoner + if summoner['gameName'].nil? || summoner['gameName'].empty? + puts 'Could not grab summoner info. Try restarting your League Client.'.light_red + ask 'Press Enter to exit.'.cyan + exit 1 + end + puts "\nYou're logged in as #{summoner['gameName']} ##{summoner['tagLine']}.".light_blue + puts $sep + puts "\nFeel free to try the options, no actions will be taken until you confirm a banner like this:".light_blue + puts 'CONFIRM: Perform this action? [y|n]'.light_magenta + puts $sep - puts $sep - puts + done = false + things_todo = { + '1' => 'Materials', + '2' => 'Champions', + '3' => 'Skins', + #"4" => "Tacticians", + '5' => 'Eternals', + '6' => 'Emotes', + '7' => 'Ward Skins', + '8' => 'Icons', + 's' => 'Open Disenchanter Global Stats', + 'r' => 'Open GitHub repository', + 'd' => 'Debug Tools', + 'x' => 'Exit', + } + things_done = [] - puts "Option chosen: #{things_todo[todo]}".light_white - - case todo - when "1" - handle_materials - when "2" - handle_champions - when "3" - handle_skins - # when "4" - # handle_tacticians - when "5" - handle_eternals - when "6" - handle_emotes - when "7" - handle_ward_skins - when "8" - handle_icons - when "s" - open_stats - when "r" - open_github - when "d" - handle_debug - when "x" - done = true + until done + todo_string = '' + things_todo.each do |k, v| + todo_string += "[#{k}] ".light_white + unless things_done.include? k + todo_string += "#{v}\n".light_cyan + else + todo_string += "#{v} (done)\n".light_green end - refresh_loot - puts $sep end - puts "That's it!".light_green - if $actions > 0 - puts "We saved you about #{$actions * 3} seconds of waiting for animations to finish.".light_green - puts $sep + todo = + user_input_check( + "\nWhat would you like to do? (Hint: do Materials first so you don't miss anything!)\n\n".light_cyan + + todo_string + 'Option: ', + things_todo.keys, + '', + '' + ) + things_done << todo + + puts $sep + puts + + puts "Option chosen: #{things_todo[todo]}".light_white + + case todo + when '1' + handle_materials + when '2' + handle_champions + when '3' + handle_skins + # when "4" + # handle_tacticians + when '5' + handle_eternals + when '6' + handle_emotes + when '7' + handle_ward_skins + when '8' + handle_icons + when 's' + open_stats + when 'r' + open_github + when 'd' + handle_debug + when 'x' + done = true end - handle_stat_submission - puts "See you next time :)".light_green - ask "Press Enter to exit.".cyan - else - puts "Assuming build environment, skipping execution...".light_yellow + refresh_loot + puts $sep end + + puts "That's it!".light_green + if $actions > 0 + puts "We saved you about #{$actions * 3} seconds of waiting for animations to finish.".light_green + puts $sep + end + handle_stat_submission + puts 'See you next time :)'.light_green + ask 'Press Enter to exit.'.cyan end def ask(q) @@ -138,23 +139,23 @@ def ask(q) end def pad(str, len, right = true) - "%#{right ? "-" : ""}#{len}s" % str + "%#{right ? '-' : ''}#{len}s" % str end def set_globals begin $port, $token = read_lockfile rescue StandardError - puts "Could not grab session!".light_red - puts "Make sure the script is in your League Client folder and that your Client is running.".light_red - ask "Press Enter to exit.".cyan + puts 'Could not grab session!'.light_red + puts 'Make sure the script is in your League Client folder and that your Client is running.'.light_red + ask 'Press Enter to exit.'.cyan exit 1 end $host = "https://127.0.0.1:#{$port}" $debug = false $sep = - "____________________________________________________________".light_black + '____________________________________________________________'.light_black $actions = 0 $s_disenchanted = 0 @@ -167,44 +168,42 @@ def set_globals $ans_yn = %w[y yes n no] $ans_y = %w[y yes] $ans_n = %w[n no] - $ans_yn_d = "[y|n]" + $ans_yn_d = '[y|n]' end def read_lockfile is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) if is_windows == 0 - lockfile = "lockfile" - puts "Trying to automatically get the path of the League Client".green + lockfile = 'lockfile' + puts 'Trying to automatically get the path of the League Client'.green begin keyname = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live" reg = Win32::Registry::HKEY_CURRENT_USER.open(keyname, - Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) - lockfile = reg['InstallLocation'] + "/lockfile" + Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) + lockfile = reg['InstallLocation'] + '/lockfile' rescue => exception - end - if lockfile == "lockfile" + if lockfile == 'lockfile' begin sc = Shortcut.open("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Riot Games\\League of Legends.lnk") scpath = sc.path.split("\\") path = scpath[0..-3].join("\\") lockfile = path + "\\League of Legends\\lockfile" rescue => exception - end end end - if lockfile == "lockfile" + if lockfile == 'lockfile' begin contents = File.read("C:\\Riot Games\\League of Legends\\" + lockfile) rescue => exception - puts "Failed to automatically get League path. Please place the script in your League Client folder.".light_red - contents = File.read(lockfile) + puts 'Failed to automatically get League path. Please place the script in your League Client folder.'.light_red + contents = File.read(lockfile) end else - contents = File.read(lockfile) + contents = File.read(lockfile) end - _leagueclient, _unk_port, port, password = contents.split(":") + _leagueclient, _unk_port, port, password = contents.split(':') token = Base64.encode64("riot:#{password.chomp}") [port, token] @@ -212,61 +211,61 @@ def read_lockfile def create_client Net::HTTP.start( - "127.0.0.1", + '127.0.0.1', $port, use_ssl: true, - verify_mode: OpenSSL::SSL::VERIFY_NONE + verify_mode: OpenSSL::SSL::VERIFY_NONE, ) { |http| yield(http) } end def req_set_headers(req) - req["Content-Type"] = "application/json" - req["Authorization"] = "Basic #{$token.chomp}" + req['Content-Type'] = 'application/json' + req['Authorization'] = "Basic #{$token.chomp}" end def check_update(version_local) begin uri = URI( - "https://api.github.com/repos/marvinscham/disenchanter/releases/latest" + 'https://api.github.com/repos/marvinscham/disenchanter/releases/latest' ) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Get.new(uri, "Content-Type": "application/json") + req = Net::HTTP::Get.new(uri, "Content-Type": 'application/json') res = http.request req ans = JSON.parse(res.body) version_local = - Gem::Version.new(version_local.delete_prefix("v").delete_suffix("-beta")) + Gem::Version.new(version_local.delete_prefix('v').delete_suffix('-beta')) version_remote = Gem::Version.new( - ans["tag_name"].delete_prefix("v").delete_suffix("-beta") + ans['tag_name'].delete_prefix('v').delete_suffix('-beta') ) if version_remote > version_local - puts "New version #{ans["tag_name"]} available!".light_yellow + puts "New version #{ans['tag_name']} available!".light_yellow if ($ans_y).include? user_input_check( - "Would you like to download the new version now?", - $ans_yn, - $ans_yn_d - ) - `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans["tag_name"]}/disenchanter_up.exe -L -o disenchanter_up.exe` - puts "Done downloading!".green + 'Would you like to download the new version now?', + $ans_yn, + $ans_yn_d + ) + `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans['tag_name']}/disenchanter_up.exe -L -o disenchanter_up.exe` + puts 'Done downloading!'.green pid = spawn("start cmd.exe @cmd /k \"disenchanter_up.exe\"") Process.detach(pid) - puts "Exiting...".light_black + puts 'Exiting...'.light_black exit end elsif version_local > version_remote - puts "Welcome to the future!".light_magenta + puts 'Welcome to the future!'.light_magenta puts "Latest remote version: v#{version_remote}".light_blue else puts "You're up to date!".green end rescue => exception - handle_exception(exception, "self update") + handle_exception(exception, 'self update') end end @@ -283,7 +282,7 @@ def request_get(path) def request_post(path, body) create_client do |http| uri = URI("#{$host}/#{path}") - req = Net::HTTP::Post.new(uri, "Content-Type": "application/json") + req = Net::HTTP::Post.new(uri, "Content-Type": 'application/json') req.body = body req_set_headers(req) res = http.request req @@ -292,15 +291,15 @@ def request_post(path, body) end def refresh_loot() - request_post("lol-loot/v1/refresh?force=true", "") + request_post('lol-loot/v1/refresh?force=true', '') end def get_current_summoner() - request_get("lol-summoner/v1/current-summoner") + request_get('lol-summoner/v1/current-summoner') end def get_player_loot() - request_get("lol-loot/v1/player-loot") + request_get('lol-loot/v1/player-loot') end def get_champion_mastery(summoner_id) @@ -327,30 +326,30 @@ def post_recipe(recipe, loot_ids, repeat) ) if $debug - File.open("disenchanter_post.json", "w") { |f| f.write(op.to_json) } - puts("Okay, written to disenchanter_post.json.") + File.open('disenchanter_post.json', 'w') { |f| f.write(op.to_json) } + puts('Okay, written to disenchanter_post.json.') end op end -def user_input_check(question, answers, answerdisplay, color_preset = "default") - input = "" +def user_input_check(question, answers, answerdisplay, color_preset = 'default') + input = '' case color_preset - when "confirm" + when 'confirm' question = "CONFIRM: #{question} ".light_magenta + "#{answerdisplay}".light_white + - ": ".light_magenta - when "default" + ': '.light_magenta + when 'default' question = "#{question} ".light_cyan + "#{answerdisplay}".light_white + - ": ".light_cyan + ': '.light_cyan end until (answers).include? input input = ask question unless (answers).include? input - puts "Invalid answer, options: ".light_red + + puts 'Invalid answer, options: '.light_red + "#{answerdisplay}".light_white end end @@ -361,20 +360,20 @@ def user_input_check(question, answers, answerdisplay, color_preset = "default") def count_loot_items(loot_items) count = 0 unless loot_items.nil? || loot_items.empty? - loot_items.each { |loot| count += loot["count"] } + loot_items.each { |loot| count += loot['count'] } end count end def get_chest_name(loot_id) chest_info = get_loot_info(loot_id) - return chest_info["localizedName"] if !chest_info["localizedName"].empty? + return chest_info['localizedName'] if !chest_info['localizedName'].empty? catalogue = { - "CHEST_128" => "Champion Capsule", - "CHEST_129" => "Glorious Champion Capsule", - "CHEST_210" => "Honor Level 4 Orb", - "CHEST_211" => "Honor Level 5 Orb" + 'CHEST_128' => 'Champion Capsule', + 'CHEST_129' => 'Glorious Champion Capsule', + 'CHEST_210' => 'Honor Level 4 Orb', + 'CHEST_211' => 'Honor Level 5 Orb', } return catalogue[loot_id] if catalogue.key?(loot_id) @@ -384,26 +383,26 @@ def get_chest_name(loot_id) def handle_exception(exception, name) puts "An error occurred while handling #{name}.".light_red - puts "Please take a screenshot and create an issue at https://github.com/marvinscham/disenchanter/issues/new".light_red + puts 'Please take a screenshot and create an issue at https://github.com/marvinscham/disenchanter/issues/new'.light_red puts "If you don't have a GitHub account, send it to dev@marvinscham.de".light_red puts exception - puts "Skipping this step...".yellow + puts 'Skipping this step...'.yellow end def handle_materials done = false things_todo = { - "1" => "Mythic Essence", - "2" => "Event Tokens", - "3" => "Key Fragments", - "4" => "Capsules", - "5" => "Mastery Tokens", - "x" => "Back to main menu" + '1' => 'Mythic Essence', + '2' => 'Event Tokens', + '3' => 'Key Fragments', + '4' => 'Capsules', + '5' => 'Mastery Tokens', + 'x' => 'Back to main menu', } things_done = [] until done - todo_string = "" + todo_string = '' things_todo.each do |k, v| todo_string += "[#{k}] ".light_white unless things_done.include? k @@ -416,10 +415,10 @@ def handle_materials todo = user_input_check( "\nWhat would you like to do?\n\n".light_cyan + todo_string + - "Option: ", + 'Option: ', things_todo.keys, - "", - "" + '', + '' ) things_done << todo @@ -429,17 +428,17 @@ def handle_materials puts "Option chosen: #{things_todo[todo]}".light_white case todo - when "1" + when '1' handle_mythic_essence - when "2" + when '2' handle_event_tokens - when "3" + when '3' handle_key_fragments - when "4" + when '4' handle_capsules - when "5" + when '5' handle_mastery_tokens - when "x" + when 'x' done = true end puts $sep @@ -449,16 +448,16 @@ def handle_materials def handle_mythic_essence begin player_loot = get_player_loot - mythic_loot_id = "CURRENCY_mythic" + mythic_loot_id = 'CURRENCY_mythic' - loot_essence = player_loot.select { |l| l["lootId"] == mythic_loot_id } + loot_essence = player_loot.select { |l| l['lootId'] == mythic_loot_id } loot_essence = loot_essence[0] - if !loot_essence.nil? && loot_essence["count"] > 0 - puts "Found #{loot_essence["count"]} Mythic Essence.".light_blue + if !loot_essence.nil? && loot_essence['count'] > 0 + puts "Found #{loot_essence['count']} Mythic Essence.".light_blue craft_mythic_type_names = [ - "Blue Essence", - "Orange Essence", - "Random Skin Shards" + 'Blue Essence', + 'Orange Essence', + 'Random Skin Shards', ] craft_mythic_type = @@ -468,85 +467,85 @@ def handle_mythic_essence "[2] #{craft_mythic_type_names[1]}\n" + "[3] #{craft_mythic_type_names[2]}\n" + "[x] Cancel\n", %w[1 2 3 x], - "[1|2|3|x]" + '[1|2|3|x]' ) - unless craft_mythic_type == "x" + unless craft_mythic_type == 'x' case craft_mythic_type # Blue Essence, Orange Essence, Random Skin Shard - when "1" - recipe_target = "CURRENCY_champion" - when "2" - recipe_target = "CURRENCY_cosmetic" - when "3" - recipe_target = "CHEST_291" + when '1' + recipe_target = 'CURRENCY_champion' + when '2' + recipe_target = 'CURRENCY_cosmetic' + when '3' + recipe_target = 'CHEST_291' end recipes = get_recipes_for_item(mythic_loot_id) recipes = - recipes.select { |r| r["outputs"][0]["lootName"] == recipe_target } + recipes.select { |r| r['outputs'][0]['lootName'] == recipe_target } unless recipes.length == 0 recipe = recipes[0] - puts "Recipe found: #{recipe["contextMenuText"]} for #{recipe["slots"][0]["quantity"]} Mythic Essence".light_blue + puts "Recipe found: #{recipe['contextMenuText']} for #{recipe['slots'][0]['quantity']} Mythic Essence".light_blue craft_mythic_amount = user_input_check( "Alright, how much Mythic Essence should we use to craft #{craft_mythic_type_names[craft_mythic_type.to_i - 1]}?", - (1..loot_essence["count"].to_i) + (1..loot_essence['count'].to_i) .to_a - .append("all") + .append('all') .map! { |n| n.to_s }, - "[1..#{loot_essence["count"]}|all]" + "[1..#{loot_essence['count']}|all]" ) - if craft_mythic_amount == "all" - craft_mythic_amount = loot_essence["count"] + if craft_mythic_amount == 'all' + craft_mythic_amount = loot_essence['count'] end craft_mythic_amount = craft_mythic_amount.to_i could_craft = - (craft_mythic_amount / recipe["slots"][0]["quantity"]).floor + (craft_mythic_amount / recipe['slots'][0]['quantity']).floor unless could_craft < 1 if ($ans_y).include? user_input_check( - "Craft #{could_craft * recipe["outputs"][0]["quantity"]} " + - "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]} from " + - "#{(craft_mythic_amount / recipe["slots"][0]["quantity"]).floor * recipe["slots"][0]["quantity"]} Mythic Essence?", - $ans_yn, - $ans_yn_d, - "confirm" - ) + "Craft #{could_craft * recipe['outputs'][0]['quantity']} " + + "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]} from " + + "#{(craft_mythic_amount / recipe['slots'][0]['quantity']).floor * recipe['slots'][0]['quantity']} Mythic Essence?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) case craft_mythic_type - when "1" + when '1' $s_blue_essence += - could_craft * recipe["outputs"][0]["quantity"] - when "2" + could_craft * recipe['outputs'][0]['quantity'] + when '2' $s_orange_essence += - could_craft * recipe["outputs"][0]["quantity"] + could_craft * recipe['outputs'][0]['quantity'] end $s_crafted += could_craft post_recipe( - recipe["recipeName"], + recipe['recipeName'], mythic_loot_id, - (craft_mythic_amount / recipe["slots"][0]["quantity"]).floor + (craft_mythic_amount / recipe['slots'][0]['quantity']).floor ) - puts "Done!".green + puts 'Done!'.green end else - puts "Not enough Mythic Essence for that recipe.".yellow + puts 'Not enough Mythic Essence for that recipe.'.yellow end else puts "Recipes for #{craft_mythic_type_names[craft_mythic_type.to_i - 1]} seem to be unavailable.".yellow end else - puts "Mythic crafting canceled.".yellow + puts 'Mythic crafting canceled.'.yellow end else - puts "Found no Mythic Essence to use.".yellow + puts 'Found no Mythic Essence to use.'.yellow end rescue => exception - handle_exception(exception, "Mythic Essence") + handle_exception(exception, 'Mythic Essence') end end @@ -556,19 +555,19 @@ def handle_event_tokens loot_event_token = player_loot.select do |l| - l["type"] == "MATERIAL" && l["displayCategories"] == "CHEST" && - l["lootId"].start_with?("MATERIAL_") && - !l["lootId"].start_with?("MATERIAL_key") + l['type'] == 'MATERIAL' && l['displayCategories'] == 'CHEST' && + l['lootId'].start_with?('MATERIAL_') && + !l['lootId'].start_with?('MATERIAL_key') end loot_event_token = loot_event_token[0] - if !loot_event_token.nil? && loot_event_token["count"] > 0 - puts "Found Event Tokens: #{loot_event_token["count"]}x #{loot_event_token["localizedName"]}".light_blue - token_recipes = get_recipes_for_item(loot_event_token["lootId"]) + if !loot_event_token.nil? && loot_event_token['count'] > 0 + puts "Found Event Tokens: #{loot_event_token['count']}x #{loot_event_token['localizedName']}".light_blue + token_recipes = get_recipes_for_item(loot_event_token['lootId']) craft_tokens_type_names = [ - "Champion Shards and Blue Essence", - "Random Emotes" + 'Champion Shards and Blue Essence', + 'Random Emotes', ] craft_tokens_type = user_input_check( @@ -576,103 +575,101 @@ def handle_event_tokens "[1] #{craft_tokens_type_names[0]}\n" + "[2] #{craft_tokens_type_names[1]}\n" + "[x] Cancel\n", %w[1 2 x], - "[1|2|x]" + '[1|2|x]' ) - unless craft_tokens_type == "x" + unless craft_tokens_type == 'x' # CHEST_187 = Random Emote # CHEST_241 = Random Champion Shard # CURRENCY_champion = Blue Essence - if craft_tokens_type == "1" + if craft_tokens_type == '1' recipe_targets = %w[CHEST_241 CURRENCY_champion] - elsif craft_tokens_type == "2" + elsif craft_tokens_type == '2' recipe_targets = %w[CHEST_187] end - token_recipes = token_recipes.select { |r| !r["outputs"][0].nil? } + token_recipes = token_recipes.select { |r| !r['outputs'][0].nil? } token_recipes = token_recipes.select do |r| - recipe_targets.include? r["outputs"][0]["lootName"] + recipe_targets.include? r['outputs'][0]['lootName'] end token_recipes = - token_recipes.sort_by { |r| r["slots"][0]["quantity"] }.reverse! + token_recipes.sort_by { |r| r['slots'][0]['quantity'] }.reverse! token_recipes.each do |r| - puts "Recipe found: #{r["contextMenuText"]} for #{r["slots"][0]["quantity"]} Tokens".light_black + puts "Recipe found: #{r['contextMenuText']} for #{r['slots'][0]['quantity']} Tokens".light_black end craft_tokens_amount = user_input_check( "Alright, how many Event Tokens should we use to craft #{craft_tokens_type_names[craft_tokens_type.to_i - 1]}?", - (1..loot_event_token["count"].to_i) + (1..loot_event_token['count'].to_i) .to_a - .append("all") + .append('all') .map! { |n| n.to_s }, - "[1..#{loot_event_token["count"]}|all]" + "[1..#{loot_event_token['count']}|all]" ) - if craft_tokens_amount == "all" - craft_tokens_amount = loot_event_token["count"] + if craft_tokens_amount == 'all' + craft_tokens_amount = loot_event_token['count'] end craft_tokens_amount = craft_tokens_amount.to_i total_could_craft = 0 token_recipes.each do |r| - r["could_craft"] = ( - craft_tokens_amount / r["slots"][0]["quantity"] - ).floor - total_could_craft += r["could_craft"] + r['could_craft'] = (craft_tokens_amount / r['slots'][0]['quantity']).floor + total_could_craft += r['could_craft'] craft_tokens_amount -= - (craft_tokens_amount / r["slots"][0]["quantity"]).floor * - r["slots"][0]["quantity"] - if r["could_craft"] > 0 - puts "We could craft #{r["could_craft"]}x #{r["contextMenuText"]} for #{r["slots"][0]["quantity"]} Tokens each.".light_green + (craft_tokens_amount / r['slots'][0]['quantity']).floor * + r['slots'][0]['quantity'] + if r['could_craft'] > 0 + puts "We could craft #{r['could_craft']}x #{r['contextMenuText']} for #{r['slots'][0]['quantity']} Tokens each.".light_green end end - token_recipes = token_recipes.select { |r| r["could_craft"] > 0 } + token_recipes = token_recipes.select { |r| r['could_craft'] > 0 } if total_could_craft > 0 if ($ans_y).include? user_input_check( - "Commit to forging?", - $ans_yn, - $ans_yn_d, - "confirm" - ) + 'Commit to forging?', + $ans_yn, + $ans_yn_d, + 'confirm' + ) token_recipes.each do |r| - if craft_tokens_type == "1" + if craft_tokens_type == '1' $s_blue_essence += - r["outputs"][0]["quantity"] * r["could_craft"] + r['outputs'][0]['quantity'] * r['could_craft'] end - $s_crafted += r["could_craft"] + $s_crafted += r['could_craft'] end threads = token_recipes.map do |r| Thread.new do post_recipe( - r["recipeName"], - loot_event_token["lootId"], - r["could_craft"] + r['recipeName'], + loot_event_token['lootId'], + r['could_craft'] ) end end threads.each(&:join) - puts "Done!".green + puts 'Done!'.green end else puts "Can't afford any recipe, skipping.".yellow end else - puts "Token crafting canceled.".yellow + puts 'Token crafting canceled.'.yellow end else - puts "Found no Event Tokens.".yellow + puts 'Found no Event Tokens.'.yellow end rescue => exception - handle_exception(exception, "Event Tokens") + handle_exception(exception, 'Event Tokens') end end @@ -681,28 +678,28 @@ def handle_key_fragments player_loot = get_player_loot loot_keys = - player_loot.select { |l| l["lootId"] == "MATERIAL_key_fragment" } + player_loot.select { |l| l['lootId'] == 'MATERIAL_key_fragment' } if count_loot_items(loot_keys) >= 3 puts "Found #{count_loot_items(loot_keys)} key fragments.".light_blue if ($ans_y).include? user_input_check( - "Craft #{(count_loot_items(loot_keys) / 3).floor} keys from #{count_loot_items(loot_keys)} key fragments?", - $ans_yn, - $ans_yn_d, - "confirm" - ) + "Craft #{(count_loot_items(loot_keys) / 3).floor} keys from #{count_loot_items(loot_keys)} key fragments?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) $s_crafted += (count_loot_items(loot_keys) / 3).floor post_recipe( - "MATERIAL_key_fragment_forge", - "MATERIAL_key_fragment", + 'MATERIAL_key_fragment_forge', + 'MATERIAL_key_fragment', (count_loot_items(loot_keys) / 3).floor ) - puts "Done!".green + puts 'Done!'.green end else - puts "Found less than 3 key fragments.".yellow + puts 'Found less than 3 key fragments.'.yellow end rescue => exception - handle_exception(exception, "Key Fragments") + handle_exception(exception, 'Key Fragments') end end @@ -711,89 +708,89 @@ def handle_capsules player_loot = get_player_loot loot_capsules = - player_loot.select { |l| l["lootName"].start_with?("CHEST_") } + player_loot.select { |l| l['lootName'].start_with?('CHEST_') } loot_capsules.each do |c| - recipes = get_recipes_for_item(c["lootId"]) - if recipes[0]["slots"].length > 1 || !recipes[0]["type"] == "OPEN" - c["needs_key"] = true + recipes = get_recipes_for_item(c['lootId']) + if recipes[0]['slots'].length > 1 || !recipes[0]['type'] == 'OPEN' + c['needs_key'] = true else - c["needs_key"] = false + c['needs_key'] = false end end - loot_capsules = loot_capsules.select { |c| c["needs_key"] == false } + loot_capsules = loot_capsules.select { |c| c['needs_key'] == false } if count_loot_items(loot_capsules) > 0 puts "Found #{count_loot_items(loot_capsules)} capsules:".light_blue loot_capsules.each do |c| - puts "#{c["count"]}x ".light_black + - "#{get_chest_name(c["lootId"])}".light_white + puts "#{c['count']}x ".light_black + + "#{get_chest_name(c['lootId'])}".light_white end if ($ans_y).include? user_input_check( - "Open #{count_loot_items(loot_capsules)} (keyless) capsules?", - $ans_yn, - $ans_yn_d, - "confirm" - ) + "Open #{count_loot_items(loot_capsules)} (keyless) capsules?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) $s_opened += count_loot_items(loot_capsules) threads = loot_capsules.map do |c| Thread.new do - res = post_recipe(c["lootId"] + "_OPEN", c["lootId"], c["count"]) - res["added"].each do |r| - if r["playerLoot"]["lootId"] == "CURRENCY_champion" - $s_blue_essence += r["deltaCount"] + res = post_recipe(c['lootId'] + '_OPEN', c['lootId'], c['count']) + res['added'].each do |r| + if r['playerLoot']['lootId'] == 'CURRENCY_champion' + $s_blue_essence += r['deltaCount'] end end end end threads.each(&:join) - puts "Done!".green + puts 'Done!'.green end else - puts "Found no keyless capsules to open.".yellow + puts 'Found no keyless capsules to open.'.yellow end rescue => exception - handle_exception(exception, "Capsules") + handle_exception(exception, 'Capsules') end end def handle_mastery_tokens begin player_loot = get_player_loot - loot_shards = player_loot.select { |l| l["type"] == "CHAMPION_RENTAL" } - loot_perms = player_loot.select { |l| l["type"] == "CHAMPION" } + loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } + loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } - recipes6 = get_recipes_for_item("CHAMPION_TOKEN_6-1") - recipes7 = get_recipes_for_item("CHAMPION_TOKEN_7-1") + recipes6 = get_recipes_for_item('CHAMPION_TOKEN_6-1') + recipes7 = get_recipes_for_item('CHAMPION_TOKEN_7-1') recipe6_cost = recipes6.select do |r| - r["recipeName"] == "CHAMPION_TOKEN_6_redeem_withessence" + r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' end - recipe6_cost = recipe6_cost[0]["slots"][1]["quantity"] + recipe6_cost = recipe6_cost[0]['slots'][1]['quantity'] recipe7_cost = recipes7.select do |r| - r["recipeName"] == "CHAMPION_TOKEN_7_redeem_withessence" + r['recipeName'] == 'CHAMPION_TOKEN_7_redeem_withessence' end - recipe7_cost = recipe7_cost[0]["slots"][1]["quantity"] + recipe7_cost = recipe7_cost[0]['slots'][1]['quantity'] loot_overall_tokens = player_loot.select do |l| - (l["lootName"] == "CHAMPION_TOKEN_6") || - (l["lootName"] == "CHAMPION_TOKEN_7") + (l['lootName'] == 'CHAMPION_TOKEN_6') || + (l['lootName'] == 'CHAMPION_TOKEN_7') end puts "Found #{count_loot_items(loot_overall_tokens)} Mastery Tokens.".light_blue loot_mastery_tokens = player_loot.select do |l| - (l["lootName"] == "CHAMPION_TOKEN_6" && l["count"] == 2) || - (l["lootName"] == "CHAMPION_TOKEN_7" && l["count"] == 3) + (l['lootName'] == 'CHAMPION_TOKEN_6' && l['count'] == 2) || + (l['lootName'] == 'CHAMPION_TOKEN_7' && l['count'] == 3) end if loot_mastery_tokens.count > 0 loot_mastery_tokens = - loot_mastery_tokens.sort_by { |l| [l["lootName"], l["itemDesc"]] } + loot_mastery_tokens.sort_by { |l| [l['lootName'], l['itemDesc']] } puts "We could upgrade the following champions:\n".light_blue needed_shards = 0 needed_perms = 0 @@ -801,35 +798,35 @@ def handle_mastery_tokens loot_mastery_tokens.each do |t| ref_shard = - loot_shards.select { |l| t["refId"] == l["storeItemId"].to_s } + loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } ref_perm = - loot_shards.select { |l| t["refId"] == l["storeItemId"].to_s } - - print pad(t["itemDesc"], 15, false).light_white - print " to Mastery Level ".light_black - print "#{(t["lootName"])[-1]}".light_white - print " using ".light_black - if !ref_shard.empty? && ref_shard[0]["count"] > 0 - print "a champion shard.".green + loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } + + print pad(t['itemDesc'], 15, false).light_white + print ' to Mastery Level '.light_black + print "#{(t['lootName'])[-1]}".light_white + print ' using '.light_black + if !ref_shard.empty? && ref_shard[0]['count'] > 0 + print 'a champion shard.'.green needed_shards += 1 - t["upgrade_type"] = "shard" - elsif !ref_perm.empty? && ref_shard[0]["count"] > 0 - print "a champion permanent.".green + t['upgrade_type'] = 'shard' + elsif !ref_perm.empty? && ref_shard[0]['count'] > 0 + print 'a champion permanent.'.green needed_perms += 1 - t["upgrade_type"] = "permanent" + t['upgrade_type'] = 'permanent' else - recipe_cost = (t["lootName"])[-1] == "6" ? recipe6_cost : recipe7_cost + recipe_cost = (t['lootName'])[-1] == '6' ? recipe6_cost : recipe7_cost print "#{recipe_cost} Blue Essence.".yellow needed_essence += recipe_cost - t["upgrade_type"] = "essence" + t['upgrade_type'] = 'essence' end puts end puts owned_essence = - player_loot.select { |l| l["lootId"] == "CURRENCY_champion" } - owned_essence = owned_essence[0]["count"] + player_loot.select { |l| l['lootId'] == 'CURRENCY_champion' } + owned_essence = owned_essence[0]['count'] if (owned_essence > needed_essence) question_string = "Upgrade #{loot_mastery_tokens.count} champions using " @@ -837,35 +834,35 @@ def handle_mastery_tokens question_string += "#{needed_perms} Permanents, " if needed_perms > 0 question_string += "#{needed_essence} Blue Essence, " if needed_essence > 0 - question_string = question_string.delete_suffix(", ") - question_string += "?" + question_string = question_string.delete_suffix(', ') + question_string += '?' if $ans_y.include? user_input_check( question_string, $ans_yn, $ans_yn_d, - "confirm" + 'confirm' ) loot_mastery_tokens.each do |t| $s_redeemed += 1 - target_level = (t["lootName"])[-1] - case t["upgrade_type"] - when "shard" + target_level = (t['lootName'])[-1] + case t['upgrade_type'] + when 'shard' post_recipe( "CHAMPION_TOKEN_#{target_level}_redeem_withshard", - [t["lootId"], "CHAMPION_RENTAL_#{t["refId"]}"], + [t['lootId'], "CHAMPION_RENTAL_#{t['refId']}"], 1 ) - when "permanent" + when 'permanent' post_recipe( "CHAMPION_TOKEN_#{target_level}_redeem_withpermanent", - [t["lootId"], "CHAMPION_#{t["refId"]}"], + [t['lootId'], "CHAMPION_#{t['refId']}"], 1 ) - when "essence" + when 'essence' post_recipe( "CHAMPION_TOKEN_#{target_level}_redeem_withessence", - [t["lootId"], "CURRENCY_champion"], + [t['lootId'], 'CURRENCY_champion'], 1 ) end @@ -875,10 +872,10 @@ def handle_mastery_tokens puts "You're missing #{needed_essence - owned_essence} Blue Essence needed to proceed. Skipping...".yellow end else - puts "Found no upgradable set of Mastery Tokens.".yellow + puts 'Found no upgradable set of Mastery Tokens.'.yellow end rescue => exception - handle_exception(exception, "token upgrades") + handle_exception(exception, 'token upgrades') end end @@ -887,13 +884,13 @@ def handle_generic(name, type, recipe) player_loot = get_player_loot disenchant_all = true - loot_generic = player_loot.select { |l| l["type"] == type } + loot_generic = player_loot.select { |l| l['type'] == type } if count_loot_items(loot_generic) > 0 puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue contains_unowned_items = false loot_generic.each do |l| - if l["redeemableStatus"] != "ALREADY_OWNED" + if l['redeemableStatus'] != 'ALREADY_OWNED' contains_unowned_items = true end end @@ -902,22 +899,22 @@ def handle_generic(name, type, recipe) user_option = user_input_check( "Keep #{name} you don't own yet?\n".light_cyan + - "[y] ".light_white + "Yes\n".light_cyan + "[n] ".light_white + - "No\n".light_cyan + "[x] ".light_white + - "Exit to main menu\n".light_cyan + "Option: ", + '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + + "No\n".light_cyan + '[x] '.light_white + + "Exit to main menu\n".light_cyan + 'Option: ', %w[y n x], - "[y|n|x]", - "" + '[y|n|x]', + '' ) case user_option - when "x" - puts "Action cancelled".yellow + when 'x' + puts 'Action cancelled'.yellow return - when "y" + when 'y' disenchant_all = false loot_generic = - loot_generic.select { |g| g["redeemableStatus"] == "ALREADY_OWNED" } + loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue end end @@ -925,46 +922,46 @@ def handle_generic(name, type, recipe) if count_loot_items(loot_generic) > 0 total_oe_value = 0 loot_generic.each do |g| - total_oe_value += g["disenchantValue"] * g["count"] + total_oe_value += g['disenchantValue'] * g['count'] end - if loot_generic[0]["itemDesc"] == "" - loot_name_index = "localizedName" + if loot_generic[0]['itemDesc'] == '' + loot_name_index = 'localizedName' else - loot_name_index = "itemDesc" + loot_name_index = 'itemDesc' end loot_generic = loot_generic.sort_by do |l| - [l["redeemableStatus"], l[loot_name_index]] + [l['redeemableStatus'], l[loot_name_index]] end puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue loot_generic.each do |l| - loot_value = l["disenchantValue"] * l["count"] - print pad("#{l["count"]}x ", 5, false).light_black + loot_value = l['disenchantValue'] * l['count'] + print pad("#{l['count']}x ", 5, false).light_black print pad("#{l[loot_name_index]}", 30).light_white - print " @ ".light_black + print ' @ '.light_black print pad("#{loot_value} OE", 8, false).light_black - if disenchant_all && l["redeemableStatus"] != "ALREADY_OWNED" - print " (not owned)".yellow + if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' + print ' (not owned)'.yellow end puts end if ($ans_y).include? user_input_check( - "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", - $ans_yn, - $ans_yn_d, - "confirm" - ) + "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) $s_disenchanted += count_loot_items(loot_generic) $s_orange_essence += total_oe_value threads = loot_generic.map do |g| - Thread.new { post_recipe(recipe, g["lootId"], g["count"]) } + Thread.new { post_recipe(recipe, g['lootId'], g['count']) } end threads.each(&:join) - puts "Done!".green + puts 'Done!'.green end else puts "Found no owned #{name} to disenchant.".yellow @@ -978,51 +975,51 @@ def handle_generic(name, type, recipe) end def handle_skins - handle_generic("Skin Shards", "SKIN_RENTAL", "SKIN_RENTAL_disenchant") - handle_generic("Skin Permanents", "SKIN", "SKIN_disenchant") + handle_generic('Skin Shards', 'SKIN_RENTAL', 'SKIN_RENTAL_disenchant') + handle_generic('Skin Permanents', 'SKIN', 'SKIN_disenchant') end def handle_eternals handle_generic( - "Eternal Shards", - "STATSTONE_SHARD", - "STATSTONE_SHARD_DISENCHANT" + 'Eternal Shards', + 'STATSTONE_SHARD', + 'STATSTONE_SHARD_DISENCHANT' ) - handle_generic("Eternals", "STATSTONE", "STATSTONE_DISENCHANT") + handle_generic('Eternals', 'STATSTONE', 'STATSTONE_DISENCHANT') end def handle_emotes - handle_generic("Emotes", "EMOTE", "EMOTE_disenchant") + handle_generic('Emotes', 'EMOTE', 'EMOTE_disenchant') end def handle_ward_skins handle_generic( - "Ward Skin Shards", - "WARDSKIN_RENTAL", - "WARDSKIN_RENTAL_disenchant" + 'Ward Skin Shards', + 'WARDSKIN_RENTAL', + 'WARDSKIN_RENTAL_disenchant' ) - handle_generic("Ward Skin Permanents", "WARDSKIN", "WARDSKIN_disenchant") + handle_generic('Ward Skin Permanents', 'WARDSKIN', 'WARDSKIN_disenchant') end def handle_icons - handle_generic("Icons", "SUMMONERICON", "SUMMONERICON_disenchant") + handle_generic('Icons', 'SUMMONERICON', 'SUMMONERICON_disenchant') end def handle_champions begin player_loot = get_player_loot - loot_shards = player_loot.select { |l| l["type"] == "CHAMPION_RENTAL" } + loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } - loot_perms = player_loot.select { |l| l["type"] == "CHAMPION" } + loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } if count_loot_items(loot_perms) > 0 if ($ans_y).include? user_input_check( - "Should we include champion permanents in this process?", - $ans_yn, - $ans_yn_d - ) + 'Should we include champion permanents in this process?', + $ans_yn, + $ans_yn_d + ) loot_shards = player_loot.select do |l| - l["type"] == "CHAMPION_RENTAL" || l["type"] == "CHAMPION" + l['type'] == 'CHAMPION_RENTAL' || l['type'] == 'CHAMPION' end end end @@ -1031,18 +1028,18 @@ def handle_champions puts "Found #{count_loot_items(loot_shards)} champion shards.".light_blue loot_shards.each do |s| - s["count_keep"] = 0 - s["disenchant_note"] = "" + s['count_keep'] = 0 + s['disenchant_note'] = '' end loot_shards_not_owned = - loot_shards.select { |s| s["redeemableStatus"] != "ALREADY_OWNED" } + loot_shards.select { |s| s['redeemableStatus'] != 'ALREADY_OWNED' } if loot_shards_not_owned.length > 0 if ($ans_y).include? user_input_check( - "Keep a shard for champions you don't own yet?", - $ans_yn, - $ans_yn_d - ) + "Keep a shard for champions you don't own yet?", + $ans_yn, + $ans_yn_d + ) loot_shards = handle_champions_owned(loot_shards) end else @@ -1050,18 +1047,15 @@ def handle_champions end disenchant_modes = { - "1" => "Disenchant all champion shards", - "2" => - "Keep enough (1/2) shards for champions you own mastery 6/7 tokens for", - "3" => - "Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)", - "4" => - "Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)", - "5" => "Keep one shard of each champion regardless of mastery", - "x" => "Cancel" + '1' => 'Disenchant all champion shards', + '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', + '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', + '4' => 'Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)', + '5' => 'Keep one shard of each champion regardless of mastery', + 'x' => 'Cancel', } - modes_string = "" + modes_string = '' disenchant_modes.each do |k, v| modes_string += "[#{k}] ".light_white modes_string += "#{v}\n".light_cyan @@ -1070,41 +1064,41 @@ def handle_champions disenchant_shards_mode = user_input_check( "Okay, which option would you like to go by?\n" + modes_string + - "Option: ", + 'Option: ', disenchant_modes.keys, - "[1|2|3|4|5|x]", - "" + '[1|2|3|4|5|x]', + '' ) - unless disenchant_shards_mode == "x" + unless disenchant_shards_mode == 'x' case disenchant_shards_mode - when "1" + when '1' # no filtering needed -> done - when "2" + when '2' loot_shards = handle_champions_tokens(player_loot, loot_shards) - when "3" + when '3' loot_shards = handle_champions_mastery(loot_shards) - when "4" + when '4' loot_shards = handle_champions_mastery(loot_shards, true) - when "5" + when '5' loot_shards = handle_champions_collection(loot_shards) end - loot_shards = loot_shards.select { |l| l["count"] > 0 } + loot_shards = loot_shards.select { |l| l['count'] > 0 } loot_shards = - loot_shards.sort_by { |l| [l["disenchant_note"], l["itemDesc"]] } + loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } if count_loot_items(loot_shards) > 0 puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue loot_shards.each do |l| - loot_value = l["disenchantValue"] * l["count"] - print pad("#{l["count"]}x ", 5, false).light_black - print pad("#{l["itemDesc"]}", 15).light_white - print " @ ".light_black + loot_value = l['disenchantValue'] * l['count'] + print pad("#{l['count']}x ", 5, false).light_black + print pad("#{l['itemDesc']}", 15).light_white + print ' @ '.light_black print pad("#{loot_value} BE", 8, false).light_black - if l["count_keep"] > 0 - puts " keeping #{l["count_keep"]}".green - elsif l["disenchant_note"].length > 0 - puts " #{l["disenchant_note"]}" + if l['count_keep'] > 0 + puts " keeping #{l['count_keep']}".green + elsif l['disenchant_note'].length > 0 + puts " #{l['disenchant_note']}" else puts end @@ -1114,7 +1108,7 @@ def handle_champions total_be_value = 0 loot_shards.each do |l| - total_be_value += l["disenchantValue"] * l["count"] + total_be_value += l['disenchantValue'] * l['count'] end if count_loot_items(loot_shards) > 0 @@ -1122,7 +1116,7 @@ def handle_champions "Disenchant #{count_loot_items(loot_shards)} champion shards for #{total_be_value} Blue Essence?", $ans_yn, $ans_yn_d, - "confirm" + 'confirm' ) $s_blue_essence += total_be_value $s_disenchanted += count_loot_items(loot_shards) @@ -1130,43 +1124,43 @@ def handle_champions loot_shards.map do |s| Thread.new do post_recipe( - "CHAMPION_RENTAL_disenchant", - s["lootId"], - s["count"] + 'CHAMPION_RENTAL_disenchant', + s['lootId'], + s['count'] ) end end threads.each(&:join) - puts "Done!".green + puts 'Done!'.green end else - puts "All remaining champions have been excluded, skipping...".yellow + puts 'All remaining champions have been excluded, skipping...'.yellow end else puts "Job's already done: no champion shards left matching your selection.".green end else - puts "Champion shard disenchanting canceled.".yellow + puts 'Champion shard disenchanting canceled.'.yellow end else - puts "Found no champion shards to disenchant.".yellow + puts 'Found no champion shards to disenchant.'.yellow end rescue => exception - handle_exception(exception, "Champion Shards") + handle_exception(exception, 'Champion Shards') end end def handle_champions_owned(loot_shards) begin loot_shards.each do |l| - unless l["redeemableStatus"] == "ALREADY_OWNED" - l["count"] -= 1 - l["count_keep"] += 1 + unless l['redeemableStatus'] == 'ALREADY_OWNED' + l['count'] -= 1 + l['count_keep'] += 1 end end - return loot_shards.select { |l| l["count"] > 0 } + return loot_shards.select { |l| l['count'] > 0 } rescue => exception - handle_capsules(exception, "Owned Champion Shards") + handle_capsules(exception, 'Owned Champion Shards') end end @@ -1176,13 +1170,13 @@ def handle_champions_tokens(player_loot, loot_shards) token7_champion_ids = [] loot_mastery_tokens = - player_loot.select { |l| l["type"] == "CHAMPION_TOKEN" } + player_loot.select { |l| l['type'] == 'CHAMPION_TOKEN' } loot_mastery_tokens.each do |token| - if token["lootName"] = "CHAMPION_TOKEN_6" - token6_champion_ids << token["refId"].to_i - elsif token["lootName"] = "CHAMPION_TOKEN_7" - token7_champion_ids << token["refId"].to_i + if token['lootName'] = 'CHAMPION_TOKEN_6' + token6_champion_ids << token['refId'].to_i + elsif token['lootName'] = 'CHAMPION_TOKEN_7' + token7_champion_ids << token['refId'].to_i end end @@ -1190,24 +1184,24 @@ def handle_champions_tokens(player_loot, loot_shards) loot_shards = loot_shards.each do |l| - if token6_champion_ids.include? l["storeItemId"] - l["count"] -= 2 - l["count_keep"] += 2 - elsif token7_champion_ids.include? l["storeItemId"] - l["count"] -= 1 - l["count_keep"] += 1 + if token6_champion_ids.include? l['storeItemId'] + l['count'] -= 2 + l['count_keep'] += 2 + elsif token7_champion_ids.include? l['storeItemId'] + l['count'] -= 1 + l['count_keep'] += 1 end end return loot_shards rescue => exception - handle_exception(exception, "Champion Shards by Tokens") + handle_exception(exception, 'Champion Shards by Tokens') end end def handle_champions_mastery(loot_shards, keep_all = false) begin summoner = get_current_summoner - player_mastery = get_champion_mastery(summoner["summonerId"]) + player_mastery = get_champion_mastery(summoner['summonerId']) threshold_champion_ids = [] mastery6_champion_ids = [] mastery7_champion_ids = [] @@ -1215,123 +1209,123 @@ def handle_champions_mastery(loot_shards, keep_all = false) unless keep_all level_threshold = user_input_check( - "Which mastery level should champions at least be for their shards to be kept?", + 'Which mastery level should champions at least be for their shards to be kept?', %w[1 2 3 4 5 6], - "[1..6]" + '[1..6]' ) else - level_threshold = "0" + level_threshold = '0' end level_threshold = level_threshold.to_i player_mastery.each do |m| - if m["championLevel"] == 7 - mastery7_champion_ids << m["championId"] - elsif m["championLevel"] == 6 - mastery6_champion_ids << m["championId"] - elsif (level_threshold..5).include? m["championLevel"] - threshold_champion_ids << m["championId"] + if m['championLevel'] == 7 + mastery7_champion_ids << m['championId'] + elsif m['championLevel'] == 6 + mastery6_champion_ids << m['championId'] + elsif (level_threshold..5).include? m['championLevel'] + threshold_champion_ids << m['championId'] elsif keep_all - threshold_champion_ids << m["championId"] + threshold_champion_ids << m['championId'] end end loot_shards.each do |l| - if mastery7_champion_ids.include? l["storeItemId"] - l["disenchant_note"] = "at mastery 7".light_black - elsif mastery6_champion_ids.include? l["storeItemId"] - l["count"] -= 1 - l["count_keep"] += 1 - elsif threshold_champion_ids.include? l["storeItemId"] - l["count"] -= 2 - l["count_keep"] += 2 + if mastery7_champion_ids.include? l['storeItemId'] + l['disenchant_note'] = 'at mastery 7'.light_black + elsif mastery6_champion_ids.include? l['storeItemId'] + l['count'] -= 1 + l['count_keep'] += 1 + elsif threshold_champion_ids.include? l['storeItemId'] + l['count'] -= 2 + l['count_keep'] += 2 else - l["disenchant_note"] = "below threshold".yellow + l['disenchant_note'] = 'below threshold'.yellow end end return loot_shards rescue => exception - handle_exception(exception, "Champion Shards by Mastery") + handle_exception(exception, 'Champion Shards by Mastery') end end def handle_champions_collection(loot_shards) begin loot_shards.each do |l| - l["count"] -= 1 - l["count_keep"] += 1 + l['count'] -= 1 + l['count_keep'] += 1 end return loot_shards rescue => exception - handle_exception(exception, "Champion Shards for Collection") + handle_exception(exception, 'Champion Shards for Collection') end end def handle_champions_exceptions(loot_shards) begin - exclusions_str = "" + exclusions_str = '' exclusions_done = false - exclusions_done_more = "" + exclusions_done_more = '' exclusions_arr = [] until exclusions_done if ($ans_y).include? user_input_check( - "Would you like to add #{exclusions_done_more}exclusions?", - $ans_yn, - $ans_yn_d - ) + "Would you like to add #{exclusions_done_more}exclusions?", + $ans_yn, + $ans_yn_d + ) exclusions_str += - "," + + ',' + ask( - "Okay, which champions? ".light_cyan + - "(case-sensitive, comma-separated)".light_white + - ": ".light_cyan + 'Okay, which champions? '.light_cyan + + '(case-sensitive, comma-separated)'.light_white + + ': '.light_cyan ) - exclusions_done_more = "more " + exclusions_done_more = 'more ' exclusions_arr = exclusions_str.split(/\s*,\s*/) exclusions_matched = - loot_shards.select { |l| exclusions_arr.include? l["itemDesc"] } - print "Exclusions recognized: ".green - exclusions_matched.each { |e| print e["itemDesc"].light_white + " " } + loot_shards.select { |l| exclusions_arr.include? l['itemDesc'] } + print 'Exclusions recognized: '.green + exclusions_matched.each { |e| print e['itemDesc'].light_white + ' ' } puts else exclusions_done = true end end loot_shards = - loot_shards.select { |l| !exclusions_arr.include? l["itemDesc"] } + loot_shards.select { |l| !exclusions_arr.include? l['itemDesc'] } return loot_shards rescue => exception - handle_exception(exception, "Champion Shard Exceptions") + handle_exception(exception, 'Champion Shard Exceptions') end end def open_github - puts "Opening GitHub repository at https://github.com/marvinscham/disenchanter/ in your browser...".light_blue - Launchy.open("https://github.com/marvinscham/disenchanter/") + puts 'Opening GitHub repository at https://github.com/marvinscham/disenchanter/ in your browser...'.light_blue + Launchy.open('https://github.com/marvinscham/disenchanter/') end def open_stats - puts "Opening Global Stats at https://github.com/marvinscham/disenchanter/wiki/Stats in your browser...".light_blue - Launchy.open("https://github.com/marvinscham/disenchanter/wiki/Stats") + puts 'Opening Global Stats at https://github.com/marvinscham/disenchanter/wiki/Stats in your browser...'.light_blue + Launchy.open('https://github.com/marvinscham/disenchanter/wiki/Stats') end def handle_debug done = false things_todo = { - "1" => "Write player_loot to file", - "2" => "Write recipes of lootId to file", - "3" => "Write loot info of lootId to file", - "m" => "Enable debug mode", - "x" => "Back to main menu" + '1' => 'Write player_loot to file', + '2' => 'Write recipes of lootId to file', + '3' => 'Write loot info of lootId to file', + 'm' => 'Enable debug mode', + 'x' => 'Back to main menu', } things_done = [] until done - todo_string = "" + todo_string = '' things_todo.each do |k, v| todo_string += "[#{k}] ".light_white unless things_done.include? k @@ -1344,10 +1338,10 @@ def handle_debug todo = user_input_check( "\nWhat would you like to do?\n\n".light_cyan + todo_string + - "Option: ", + 'Option: ', things_todo.keys, - "", - "" + '', + '' ) things_done << todo @@ -1357,38 +1351,38 @@ def handle_debug puts "Option chosen: #{things_todo[todo]}".light_white case todo - when "1" + when '1' player_loot = get_player_loot - File.open("disenchanter_loot.json", "w") do |f| + File.open('disenchanter_loot.json', 'w') do |f| f.write(player_loot.to_json) end - puts("Okay, written to disenchanter_loot.json.") - when "2" + puts('Okay, written to disenchanter_loot.json.') + when '2' loot_id = ask("Which lootId would you like the recipes for?\n".light_cyan) recipes = get_recipes_for_item loot_id - File.open("disenchanter_recipes.json", "w") do |f| + File.open('disenchanter_recipes.json', 'w') do |f| f.write(recipes.to_json) end - puts("Okay, written to disenchanter_recipes.json.") - when "3" + puts('Okay, written to disenchanter_recipes.json.') + when '3' loot_id = ask("Which lootId would you like the info for?\n".light_cyan) loot_info = get_loot_info loot_id - File.open("disenchanter_lootinfo.json", "w") do |f| + File.open('disenchanter_lootinfo.json', 'w') do |f| f.write(loot_info.to_json) end - puts("Okay, written to disenchanter_lootinfo.json.") - when "m" + puts('Okay, written to disenchanter_lootinfo.json.') + when 'm' $debug = true - puts "Debug mode enabled." - when "x" + puts 'Debug mode enabled.' + when 'x' done = true end puts $sep @@ -1401,34 +1395,34 @@ def handle_stat_submission numlen = 7 stats_string = "Your stats:\n".light_blue stats_string += - pad("Actions", strlen) + pad($actions.to_s, numlen, false).light_white + + pad('Actions', strlen) + pad($actions.to_s, numlen, false).light_white + "\n" stats_string += - pad("Disenchanted", strlen) + + pad('Disenchanted', strlen) + pad($s_disenchanted.to_s, numlen, false).light_white + "\n" stats_string += - pad("Opened", strlen) + pad($s_opened.to_s, numlen, false).light_white + + pad('Opened', strlen) + pad($s_opened.to_s, numlen, false).light_white + "\n" stats_string += - pad("Crafted", strlen) + pad($s_crafted.to_s, numlen, false).light_white + + pad('Crafted', strlen) + pad($s_crafted.to_s, numlen, false).light_white + "\n" stats_string += - pad("Redeemed", strlen) + + pad('Redeemed', strlen) + pad($s_redeemed.to_s, numlen, false).light_white + "\n" stats_string += - pad("Blue Essence", strlen) + + pad('Blue Essence', strlen) + pad($s_blue_essence.to_s, numlen, false).light_white + "\n" stats_string += - pad("Orange Essence", strlen) + + pad('Orange Essence', strlen) + pad($s_orange_essence.to_s, numlen, false).light_white + "\n" if ($ans_y).include? user_input_check( - "Would you like to contribute your (anonymous) stats to the global stats?\n".light_cyan + - stats_string + "[y|n]: ", - $ans_yn, - $ans_yn_d, - "" - ) + "Would you like to contribute your (anonymous) stats to the global stats?\n".light_cyan + + stats_string + '[y|n]: ', + $ans_yn, + $ans_yn_d, + '' + ) submit_stats( $actions, $s_disenchanted, @@ -1438,23 +1432,23 @@ def handle_stat_submission $s_blue_essence, $s_orange_essence ) - puts "Thank you very much!".light_green + puts 'Thank you very much!'.light_green end end end def submit_stats(a, d, o, c, r, be, oe) begin - uri = URI("https://checksch.de/hook/disenchanter.php") + uri = URI('https://checksch.de/hook/disenchanter.php') http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Post.new(uri, "Content-Type": "application/json") + req = Net::HTTP::Post.new(uri, "Content-Type": 'application/json') req.body = { a: a, d: d, o: o, c: c, r: r, be: be, oe: oe }.to_json http.request(req) rescue => exception - handle_exception(exception, "stat submission") + handle_exception(exception, 'stat submission') end end From ef1d4da064fa625d61dd8bce290497e3bfa62cc0 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Wed, 24 Jan 2024 20:33:03 +0100 Subject: [PATCH 19/63] =?UTF-8?q?Bump=20version:=201.5.0=20=E2=86=92=201.6?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- disenchanter.rb | 2 +- sonar-project.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 903dcae..bd7375a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.5.0 +current_version = 1.6.0 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(-(?P[a-z]+))? commit = True tag = False diff --git a/disenchanter.rb b/disenchanter.rb index 4e94095..2a536e1 100644 --- a/disenchanter.rb +++ b/disenchanter.rb @@ -28,7 +28,7 @@ def run end set_globals - current_version = 'v1.5.0' + current_version = 'v1.6.0' puts 'Hi! :)'.light_green puts "Running Disenchanter #{current_version} on port #{$port}".light_blue diff --git a/sonar-project.properties b/sonar-project.properties index 97a6d48..bb3bc54 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,5 +1,5 @@ sonar.projectKey=marvinscham_disenchanter_AY088C575jK4ghmshIlp -sonar.projectVersion=v1.5.0 +sonar.projectVersion=v1.6.0 sonar.sources=. sonar.sourceEncoding=UTF-8 From 8252048326ffbe8c20421692dab3b1226f97ce64 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 25 Jan 2024 02:17:34 +0100 Subject: [PATCH 20/63] Partial refactoring --- .gitignore | 1 - .rubocop.yml | 4 +- Gemfile.lock | 4 +- disenchanter.rb | 1455 ----------------- disenchanter_up.rb | 54 - scripts/build_main.sh | 17 + scripts/build_updater.sh | 17 + src/main.rb | 166 ++ src/modules/client_api.rb | 78 + src/modules/common_strings.rb | 9 + src/modules/debug.rb | 77 + src/modules/detect_client.rb | 57 + src/modules/handlers.rb | 12 + src/modules/handlers/champions.rb | 152 ++ src/modules/handlers/champions/collection.rb | 12 + src/modules/handlers/champions/exceptions.rb | 41 + src/modules/handlers/champions/mastery.rb | 53 + src/modules/handlers/champions/owned.rb | 15 + src/modules/handlers/champions/tokens.rb | 35 + src/modules/handlers/emotes.rb | 5 + src/modules/handlers/eternals.rb | 10 + src/modules/handlers/exception.rb | 9 + src/modules/handlers/generic.rb | 96 ++ src/modules/handlers/icons.rb | 5 + src/modules/handlers/materials.rb | 59 + src/modules/handlers/materials/capsules.rb | 53 + .../handlers/materials/key_fragments.rb | 31 + .../handlers/materials/mastery_tokens.rb | 125 ++ .../handlers/materials/mythic_essence.rb | 105 ++ src/modules/handlers/skins.rb | 6 + src/modules/handlers/wards.rb | 10 + src/modules/loot_metainfo.rb | 25 + src/modules/open_url.rb | 11 + src/modules/stat_submission.rb | 64 + src/modules/update/backwards_compat.rb | 20 + src/modules/update/checker.rb | 47 + src/modules/update/download.rb | 22 + src/modules/user_input.rb | 32 + src/updater.rb | 29 + 39 files changed, 1511 insertions(+), 1512 deletions(-) delete mode 100644 disenchanter.rb delete mode 100644 disenchanter_up.rb create mode 100644 scripts/build_main.sh create mode 100644 scripts/build_updater.sh create mode 100644 src/main.rb create mode 100644 src/modules/client_api.rb create mode 100644 src/modules/common_strings.rb create mode 100644 src/modules/debug.rb create mode 100644 src/modules/detect_client.rb create mode 100644 src/modules/handlers.rb create mode 100644 src/modules/handlers/champions.rb create mode 100644 src/modules/handlers/champions/collection.rb create mode 100644 src/modules/handlers/champions/exceptions.rb create mode 100644 src/modules/handlers/champions/mastery.rb create mode 100644 src/modules/handlers/champions/owned.rb create mode 100644 src/modules/handlers/champions/tokens.rb create mode 100644 src/modules/handlers/emotes.rb create mode 100644 src/modules/handlers/eternals.rb create mode 100644 src/modules/handlers/exception.rb create mode 100644 src/modules/handlers/generic.rb create mode 100644 src/modules/handlers/icons.rb create mode 100644 src/modules/handlers/materials.rb create mode 100644 src/modules/handlers/materials/capsules.rb create mode 100644 src/modules/handlers/materials/key_fragments.rb create mode 100644 src/modules/handlers/materials/mastery_tokens.rb create mode 100644 src/modules/handlers/materials/mythic_essence.rb create mode 100644 src/modules/handlers/skins.rb create mode 100644 src/modules/handlers/wards.rb create mode 100644 src/modules/loot_metainfo.rb create mode 100644 src/modules/open_url.rb create mode 100644 src/modules/stat_submission.rb create mode 100644 src/modules/update/backwards_compat.rb create mode 100644 src/modules/update/checker.rb create mode 100644 src/modules/update/download.rb create mode 100644 src/modules/user_input.rb create mode 100644 src/updater.rb diff --git a/.gitignore b/.gitignore index 20b3c8c..32268dd 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,6 @@ tmpout ## Specific to RubyMotion: .dat* .repl_history -.build.lockfile build/ *.bridgesupport build-iPhoneOS/ diff --git a/.rubocop.yml b/.rubocop.yml index 4d0d314..fa25733 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,2 +1,4 @@ -Style/EndOfLine: +AllCops: + NewCops: enable +Layout/EndOfLine: EnforcedStyle: lf diff --git a/Gemfile.lock b/Gemfile.lock index 8934797..8c9624f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,9 +41,10 @@ GEM date unicode-display_width (2.4.2) uri (0.11.0) + win32-shortcut (0.3.0) PLATFORMS - x64-unknown + x64-mingw-ucrt x64-unknown x86_64-linux @@ -56,6 +57,7 @@ DEPENDENCIES open-uri (~> 0.2.0) rubocop (~> 1.50) rufo (>= 0.13.0) + win32-shortcut (~> 0.3.0) RUBY VERSION ruby 3.1.2p20 diff --git a/disenchanter.rb b/disenchanter.rb deleted file mode 100644 index 2a536e1..0000000 --- a/disenchanter.rb +++ /dev/null @@ -1,1455 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'net/https' -require 'base64' -require 'json' -require 'colorize' -require 'launchy' -require 'open-uri' -require 'rbconfig' -require 'win32/registry' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ -require 'win32/shortcut' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ - -include Win32 if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ - -if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) - module Win32::Registry::Constants - KEY_WOW64_64KEY = 0x0100 - KEY_WOW64_32KEY = 0x0200 - end -end - -def run - if File.exist?('.build.lockfile') - puts 'Detected build environment, skipping execution...'.light_yellow - sleep 2 - exit - end - - set_globals - current_version = 'v1.6.0' - - puts 'Hi! :)'.light_green - puts "Running Disenchanter #{current_version} on port #{$port}".light_blue - puts 'You can exit this script at any point by pressing '.light_blue + - '[CTRL + C]'.light_white + '.'.light_blue - check_update(current_version) - puts $sep - - summoner = get_current_summoner - if summoner['gameName'].nil? || summoner['gameName'].empty? - puts 'Could not grab summoner info. Try restarting your League Client.'.light_red - ask 'Press Enter to exit.'.cyan - exit 1 - end - puts "\nYou're logged in as #{summoner['gameName']} ##{summoner['tagLine']}.".light_blue - puts $sep - puts "\nFeel free to try the options, no actions will be taken until you confirm a banner like this:".light_blue - puts 'CONFIRM: Perform this action? [y|n]'.light_magenta - puts $sep - - done = false - things_todo = { - '1' => 'Materials', - '2' => 'Champions', - '3' => 'Skins', - #"4" => "Tacticians", - '5' => 'Eternals', - '6' => 'Emotes', - '7' => 'Ward Skins', - '8' => 'Icons', - 's' => 'Open Disenchanter Global Stats', - 'r' => 'Open GitHub repository', - 'd' => 'Debug Tools', - 'x' => 'Exit', - } - things_done = [] - - until done - todo_string = '' - things_todo.each do |k, v| - todo_string += "[#{k}] ".light_white - unless things_done.include? k - todo_string += "#{v}\n".light_cyan - else - todo_string += "#{v} (done)\n".light_green - end - end - - todo = - user_input_check( - "\nWhat would you like to do? (Hint: do Materials first so you don't miss anything!)\n\n".light_cyan + - todo_string + 'Option: ', - things_todo.keys, - '', - '' - ) - things_done << todo - - puts $sep - puts - - puts "Option chosen: #{things_todo[todo]}".light_white - - case todo - when '1' - handle_materials - when '2' - handle_champions - when '3' - handle_skins - # when "4" - # handle_tacticians - when '5' - handle_eternals - when '6' - handle_emotes - when '7' - handle_ward_skins - when '8' - handle_icons - when 's' - open_stats - when 'r' - open_github - when 'd' - handle_debug - when 'x' - done = true - end - refresh_loot - puts $sep - end - - puts "That's it!".light_green - if $actions > 0 - puts "We saved you about #{$actions * 3} seconds of waiting for animations to finish.".light_green - puts $sep - end - handle_stat_submission - puts 'See you next time :)'.light_green - ask 'Press Enter to exit.'.cyan -end - -def ask(q) - print(q) - q = gets - q.chomp -end - -def pad(str, len, right = true) - "%#{right ? '-' : ''}#{len}s" % str -end - -def set_globals - begin - $port, $token = read_lockfile - rescue StandardError - puts 'Could not grab session!'.light_red - puts 'Make sure the script is in your League Client folder and that your Client is running.'.light_red - ask 'Press Enter to exit.'.cyan - exit 1 - end - $host = "https://127.0.0.1:#{$port}" - $debug = false - - $sep = - '____________________________________________________________'.light_black - - $actions = 0 - $s_disenchanted = 0 - $s_opened = 0 - $s_crafted = 0 - $s_redeemed = 0 - $s_blue_essence = 0 - $s_orange_essence = 0 - - $ans_yn = %w[y yes n no] - $ans_y = %w[y yes] - $ans_n = %w[n no] - $ans_yn_d = '[y|n]' -end - -def read_lockfile - is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) - if is_windows == 0 - lockfile = 'lockfile' - puts 'Trying to automatically get the path of the League Client'.green - begin - keyname = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live" - reg = Win32::Registry::HKEY_CURRENT_USER.open(keyname, - Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) - lockfile = reg['InstallLocation'] + '/lockfile' - rescue => exception - end - if lockfile == 'lockfile' - begin - sc = Shortcut.open("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Riot Games\\League of Legends.lnk") - scpath = sc.path.split("\\") - path = scpath[0..-3].join("\\") - lockfile = path + "\\League of Legends\\lockfile" - rescue => exception - end - end - end - if lockfile == 'lockfile' - begin - contents = File.read("C:\\Riot Games\\League of Legends\\" + lockfile) - rescue => exception - puts 'Failed to automatically get League path. Please place the script in your League Client folder.'.light_red - contents = File.read(lockfile) - end - else - contents = File.read(lockfile) - end - _leagueclient, _unk_port, port, password = contents.split(':') - token = Base64.encode64("riot:#{password.chomp}") - - [port, token] -end - -def create_client - Net::HTTP.start( - '127.0.0.1', - $port, - use_ssl: true, - verify_mode: OpenSSL::SSL::VERIFY_NONE, - ) { |http| yield(http) } -end - -def req_set_headers(req) - req['Content-Type'] = 'application/json' - req['Authorization'] = "Basic #{$token.chomp}" -end - -def check_update(version_local) - begin - uri = - URI( - 'https://api.github.com/repos/marvinscham/disenchanter/releases/latest' - ) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Get.new(uri, "Content-Type": 'application/json') - res = http.request req - ans = JSON.parse(res.body) - - version_local = - Gem::Version.new(version_local.delete_prefix('v').delete_suffix('-beta')) - version_remote = - Gem::Version.new( - ans['tag_name'].delete_prefix('v').delete_suffix('-beta') - ) - - if version_remote > version_local - puts "New version #{ans['tag_name']} available!".light_yellow - if ($ans_y).include? user_input_check( - 'Would you like to download the new version now?', - $ans_yn, - $ans_yn_d - ) - `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans['tag_name']}/disenchanter_up.exe -L -o disenchanter_up.exe` - puts 'Done downloading!'.green - - pid = spawn("start cmd.exe @cmd /k \"disenchanter_up.exe\"") - Process.detach(pid) - puts 'Exiting...'.light_black - exit - end - elsif version_local > version_remote - puts 'Welcome to the future!'.light_magenta - puts "Latest remote version: v#{version_remote}".light_blue - else - puts "You're up to date!".green - end - rescue => exception - handle_exception(exception, 'self update') - end -end - -def request_get(path) - create_client do |http| - uri = URI("#{$host}/#{path}") - req = Net::HTTP::Get.new(uri) - req_set_headers(req) - res = http.request req - JSON.parse(res.body) - end -end - -def request_post(path, body) - create_client do |http| - uri = URI("#{$host}/#{path}") - req = Net::HTTP::Post.new(uri, "Content-Type": 'application/json') - req.body = body - req_set_headers(req) - res = http.request req - JSON.parse(res.body) - end -end - -def refresh_loot() - request_post('lol-loot/v1/refresh?force=true', '') -end - -def get_current_summoner() - request_get('lol-summoner/v1/current-summoner') -end - -def get_player_loot() - request_get('lol-loot/v1/player-loot') -end - -def get_champion_mastery(summoner_id) - request_get("lol-collections/v1/inventories/#{summoner_id}/champion-mastery") -end - -def get_loot_info(loot_id) - request_get("lol-loot/v1/player-loot/#{loot_id}") -end - -def get_recipes_for_item(loot_id) - request_get("lol-loot/v1/recipes/initial-item/#{loot_id}") -end - -def post_recipe(recipe, loot_ids, repeat) - $actions += repeat - - loot_id_string = "[\"" + Array(loot_ids).join("\", \"") + "\"]" - - op = - request_post( - "lol-loot/v1/recipes/#{recipe}/craft?repeat=#{repeat}", - loot_id_string - ) - - if $debug - File.open('disenchanter_post.json', 'w') { |f| f.write(op.to_json) } - puts('Okay, written to disenchanter_post.json.') - end - op -end - -def user_input_check(question, answers, answerdisplay, color_preset = 'default') - input = '' - - case color_preset - when 'confirm' - question = - "CONFIRM: #{question} ".light_magenta + "#{answerdisplay}".light_white + - ': '.light_magenta - when 'default' - question = - "#{question} ".light_cyan + "#{answerdisplay}".light_white + - ': '.light_cyan - end - - until (answers).include? input - input = ask question - unless (answers).include? input - puts 'Invalid answer, options: '.light_red + - "#{answerdisplay}".light_white - end - end - - input -end - -def count_loot_items(loot_items) - count = 0 - unless loot_items.nil? || loot_items.empty? - loot_items.each { |loot| count += loot['count'] } - end - count -end - -def get_chest_name(loot_id) - chest_info = get_loot_info(loot_id) - return chest_info['localizedName'] if !chest_info['localizedName'].empty? - - catalogue = { - 'CHEST_128' => 'Champion Capsule', - 'CHEST_129' => 'Glorious Champion Capsule', - 'CHEST_210' => 'Honor Level 4 Orb', - 'CHEST_211' => 'Honor Level 5 Orb', - } - - return catalogue[loot_id] if catalogue.key?(loot_id) - - return loot_id -end - -def handle_exception(exception, name) - puts "An error occurred while handling #{name}.".light_red - puts 'Please take a screenshot and create an issue at https://github.com/marvinscham/disenchanter/issues/new'.light_red - puts "If you don't have a GitHub account, send it to dev@marvinscham.de".light_red - puts exception - puts 'Skipping this step...'.yellow -end - -def handle_materials - done = false - things_todo = { - '1' => 'Mythic Essence', - '2' => 'Event Tokens', - '3' => 'Key Fragments', - '4' => 'Capsules', - '5' => 'Mastery Tokens', - 'x' => 'Back to main menu', - } - things_done = [] - - until done - todo_string = '' - things_todo.each do |k, v| - todo_string += "[#{k}] ".light_white - unless things_done.include? k - todo_string += "#{v}\n".light_cyan - else - todo_string += "#{v} (done)\n".light_green - end - end - - todo = - user_input_check( - "\nWhat would you like to do?\n\n".light_cyan + todo_string + - 'Option: ', - things_todo.keys, - '', - '' - ) - things_done << todo - - puts $sep - puts - - puts "Option chosen: #{things_todo[todo]}".light_white - - case todo - when '1' - handle_mythic_essence - when '2' - handle_event_tokens - when '3' - handle_key_fragments - when '4' - handle_capsules - when '5' - handle_mastery_tokens - when 'x' - done = true - end - puts $sep - end -end - -def handle_mythic_essence - begin - player_loot = get_player_loot - mythic_loot_id = 'CURRENCY_mythic' - - loot_essence = player_loot.select { |l| l['lootId'] == mythic_loot_id } - loot_essence = loot_essence[0] - if !loot_essence.nil? && loot_essence['count'] > 0 - puts "Found #{loot_essence['count']} Mythic Essence.".light_blue - craft_mythic_type_names = [ - 'Blue Essence', - 'Orange Essence', - 'Random Skin Shards', - ] - - craft_mythic_type = - user_input_check( - "Okay, what would you like to craft?\n" + - "[1] #{craft_mythic_type_names[0]}\n" + - "[2] #{craft_mythic_type_names[1]}\n" + - "[3] #{craft_mythic_type_names[2]}\n" + "[x] Cancel\n", - %w[1 2 3 x], - '[1|2|3|x]' - ) - - unless craft_mythic_type == 'x' - case craft_mythic_type - # Blue Essence, Orange Essence, Random Skin Shard - when '1' - recipe_target = 'CURRENCY_champion' - when '2' - recipe_target = 'CURRENCY_cosmetic' - when '3' - recipe_target = 'CHEST_291' - end - - recipes = get_recipes_for_item(mythic_loot_id) - recipes = - recipes.select { |r| r['outputs'][0]['lootName'] == recipe_target } - unless recipes.length == 0 - recipe = recipes[0] - - puts "Recipe found: #{recipe['contextMenuText']} for #{recipe['slots'][0]['quantity']} Mythic Essence".light_blue - - craft_mythic_amount = - user_input_check( - "Alright, how much Mythic Essence should we use to craft #{craft_mythic_type_names[craft_mythic_type.to_i - 1]}?", - (1..loot_essence['count'].to_i) - .to_a - .append('all') - .map! { |n| n.to_s }, - "[1..#{loot_essence['count']}|all]" - ) - - if craft_mythic_amount == 'all' - craft_mythic_amount = loot_essence['count'] - end - craft_mythic_amount = craft_mythic_amount.to_i - - could_craft = - (craft_mythic_amount / recipe['slots'][0]['quantity']).floor - unless could_craft < 1 - if ($ans_y).include? user_input_check( - "Craft #{could_craft * recipe['outputs'][0]['quantity']} " + - "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]} from " + - "#{(craft_mythic_amount / recipe['slots'][0]['quantity']).floor * recipe['slots'][0]['quantity']} Mythic Essence?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - case craft_mythic_type - when '1' - $s_blue_essence += - could_craft * recipe['outputs'][0]['quantity'] - when '2' - $s_orange_essence += - could_craft * recipe['outputs'][0]['quantity'] - end - $s_crafted += could_craft - - post_recipe( - recipe['recipeName'], - mythic_loot_id, - (craft_mythic_amount / recipe['slots'][0]['quantity']).floor - ) - puts 'Done!'.green - end - else - puts 'Not enough Mythic Essence for that recipe.'.yellow - end - else - puts "Recipes for #{craft_mythic_type_names[craft_mythic_type.to_i - 1]} seem to be unavailable.".yellow - end - else - puts 'Mythic crafting canceled.'.yellow - end - else - puts 'Found no Mythic Essence to use.'.yellow - end - rescue => exception - handle_exception(exception, 'Mythic Essence') - end -end - -def handle_event_tokens - begin - player_loot = get_player_loot - - loot_event_token = - player_loot.select do |l| - l['type'] == 'MATERIAL' && l['displayCategories'] == 'CHEST' && - l['lootId'].start_with?('MATERIAL_') && - !l['lootId'].start_with?('MATERIAL_key') - end - loot_event_token = loot_event_token[0] - - if !loot_event_token.nil? && loot_event_token['count'] > 0 - puts "Found Event Tokens: #{loot_event_token['count']}x #{loot_event_token['localizedName']}".light_blue - token_recipes = get_recipes_for_item(loot_event_token['lootId']) - - craft_tokens_type_names = [ - 'Champion Shards and Blue Essence', - 'Random Emotes', - ] - craft_tokens_type = - user_input_check( - "Okay, what would you like to craft?\n" + - "[1] #{craft_tokens_type_names[0]}\n" + - "[2] #{craft_tokens_type_names[1]}\n" + "[x] Cancel\n", - %w[1 2 x], - '[1|2|x]' - ) - - unless craft_tokens_type == 'x' - # CHEST_187 = Random Emote - # CHEST_241 = Random Champion Shard - # CURRENCY_champion = Blue Essence - if craft_tokens_type == '1' - recipe_targets = %w[CHEST_241 CURRENCY_champion] - elsif craft_tokens_type == '2' - recipe_targets = %w[CHEST_187] - end - - token_recipes = token_recipes.select { |r| !r['outputs'][0].nil? } - - token_recipes = - token_recipes.select do |r| - recipe_targets.include? r['outputs'][0]['lootName'] - end - token_recipes = - token_recipes.sort_by { |r| r['slots'][0]['quantity'] }.reverse! - - token_recipes.each do |r| - puts "Recipe found: #{r['contextMenuText']} for #{r['slots'][0]['quantity']} Tokens".light_black - end - - craft_tokens_amount = - user_input_check( - "Alright, how many Event Tokens should we use to craft #{craft_tokens_type_names[craft_tokens_type.to_i - 1]}?", - (1..loot_event_token['count'].to_i) - .to_a - .append('all') - .map! { |n| n.to_s }, - "[1..#{loot_event_token['count']}|all]" - ) - - if craft_tokens_amount == 'all' - craft_tokens_amount = loot_event_token['count'] - end - craft_tokens_amount = craft_tokens_amount.to_i - - total_could_craft = 0 - - token_recipes.each do |r| - r['could_craft'] = (craft_tokens_amount / r['slots'][0]['quantity']).floor - total_could_craft += r['could_craft'] - craft_tokens_amount -= - (craft_tokens_amount / r['slots'][0]['quantity']).floor * - r['slots'][0]['quantity'] - if r['could_craft'] > 0 - puts "We could craft #{r['could_craft']}x #{r['contextMenuText']} for #{r['slots'][0]['quantity']} Tokens each.".light_green - end - end - - token_recipes = token_recipes.select { |r| r['could_craft'] > 0 } - - if total_could_craft > 0 - if ($ans_y).include? user_input_check( - 'Commit to forging?', - $ans_yn, - $ans_yn_d, - 'confirm' - ) - token_recipes.each do |r| - if craft_tokens_type == '1' - $s_blue_essence += - r['outputs'][0]['quantity'] * r['could_craft'] - end - $s_crafted += r['could_craft'] - end - - threads = - token_recipes.map do |r| - Thread.new do - post_recipe( - r['recipeName'], - loot_event_token['lootId'], - r['could_craft'] - ) - end - end - threads.each(&:join) - puts 'Done!'.green - end - else - puts "Can't afford any recipe, skipping.".yellow - end - else - puts 'Token crafting canceled.'.yellow - end - else - puts 'Found no Event Tokens.'.yellow - end - rescue => exception - handle_exception(exception, 'Event Tokens') - end -end - -def handle_key_fragments - begin - player_loot = get_player_loot - - loot_keys = - player_loot.select { |l| l['lootId'] == 'MATERIAL_key_fragment' } - if count_loot_items(loot_keys) >= 3 - puts "Found #{count_loot_items(loot_keys)} key fragments.".light_blue - if ($ans_y).include? user_input_check( - "Craft #{(count_loot_items(loot_keys) / 3).floor} keys from #{count_loot_items(loot_keys)} key fragments?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - $s_crafted += (count_loot_items(loot_keys) / 3).floor - post_recipe( - 'MATERIAL_key_fragment_forge', - 'MATERIAL_key_fragment', - (count_loot_items(loot_keys) / 3).floor - ) - puts 'Done!'.green - end - else - puts 'Found less than 3 key fragments.'.yellow - end - rescue => exception - handle_exception(exception, 'Key Fragments') - end -end - -def handle_capsules - begin - player_loot = get_player_loot - - loot_capsules = - player_loot.select { |l| l['lootName'].start_with?('CHEST_') } - loot_capsules.each do |c| - recipes = get_recipes_for_item(c['lootId']) - if recipes[0]['slots'].length > 1 || !recipes[0]['type'] == 'OPEN' - c['needs_key'] = true - else - c['needs_key'] = false - end - end - loot_capsules = loot_capsules.select { |c| c['needs_key'] == false } - - if count_loot_items(loot_capsules) > 0 - puts "Found #{count_loot_items(loot_capsules)} capsules:".light_blue - loot_capsules.each do |c| - puts "#{c['count']}x ".light_black + - "#{get_chest_name(c['lootId'])}".light_white - end - - if ($ans_y).include? user_input_check( - "Open #{count_loot_items(loot_capsules)} (keyless) capsules?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - $s_opened += count_loot_items(loot_capsules) - threads = - loot_capsules.map do |c| - Thread.new do - res = post_recipe(c['lootId'] + '_OPEN', c['lootId'], c['count']) - res['added'].each do |r| - if r['playerLoot']['lootId'] == 'CURRENCY_champion' - $s_blue_essence += r['deltaCount'] - end - end - end - end - threads.each(&:join) - puts 'Done!'.green - end - else - puts 'Found no keyless capsules to open.'.yellow - end - rescue => exception - handle_exception(exception, 'Capsules') - end -end - -def handle_mastery_tokens - begin - player_loot = get_player_loot - loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } - loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } - - recipes6 = get_recipes_for_item('CHAMPION_TOKEN_6-1') - recipes7 = get_recipes_for_item('CHAMPION_TOKEN_7-1') - recipe6_cost = - recipes6.select do |r| - r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' - end - recipe6_cost = recipe6_cost[0]['slots'][1]['quantity'] - recipe7_cost = - recipes7.select do |r| - r['recipeName'] == 'CHAMPION_TOKEN_7_redeem_withessence' - end - recipe7_cost = recipe7_cost[0]['slots'][1]['quantity'] - - loot_overall_tokens = - player_loot.select do |l| - (l['lootName'] == 'CHAMPION_TOKEN_6') || - (l['lootName'] == 'CHAMPION_TOKEN_7') - end - - puts "Found #{count_loot_items(loot_overall_tokens)} Mastery Tokens.".light_blue - - loot_mastery_tokens = - player_loot.select do |l| - (l['lootName'] == 'CHAMPION_TOKEN_6' && l['count'] == 2) || - (l['lootName'] == 'CHAMPION_TOKEN_7' && l['count'] == 3) - end - - if loot_mastery_tokens.count > 0 - loot_mastery_tokens = - loot_mastery_tokens.sort_by { |l| [l['lootName'], l['itemDesc']] } - puts "We could upgrade the following champions:\n".light_blue - needed_shards = 0 - needed_perms = 0 - needed_essence = 0 - - loot_mastery_tokens.each do |t| - ref_shard = - loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } - ref_perm = - loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } - - print pad(t['itemDesc'], 15, false).light_white - print ' to Mastery Level '.light_black - print "#{(t['lootName'])[-1]}".light_white - print ' using '.light_black - if !ref_shard.empty? && ref_shard[0]['count'] > 0 - print 'a champion shard.'.green - needed_shards += 1 - t['upgrade_type'] = 'shard' - elsif !ref_perm.empty? && ref_shard[0]['count'] > 0 - print 'a champion permanent.'.green - needed_perms += 1 - t['upgrade_type'] = 'permanent' - else - recipe_cost = (t['lootName'])[-1] == '6' ? recipe6_cost : recipe7_cost - print "#{recipe_cost} Blue Essence.".yellow - needed_essence += recipe_cost - t['upgrade_type'] = 'essence' - end - puts - end - puts - - owned_essence = - player_loot.select { |l| l['lootId'] == 'CURRENCY_champion' } - owned_essence = owned_essence[0]['count'] - if (owned_essence > needed_essence) - question_string = - "Upgrade #{loot_mastery_tokens.count} champions using " - question_string += "#{needed_shards} Shards, " if needed_shards > 0 - question_string += "#{needed_perms} Permanents, " if needed_perms > 0 - question_string += - "#{needed_essence} Blue Essence, " if needed_essence > 0 - question_string = question_string.delete_suffix(', ') - question_string += '?' - - if $ans_y.include? user_input_check( - question_string, - $ans_yn, - $ans_yn_d, - 'confirm' - ) - loot_mastery_tokens.each do |t| - $s_redeemed += 1 - target_level = (t['lootName'])[-1] - case t['upgrade_type'] - when 'shard' - post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withshard", - [t['lootId'], "CHAMPION_RENTAL_#{t['refId']}"], - 1 - ) - when 'permanent' - post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withpermanent", - [t['lootId'], "CHAMPION_#{t['refId']}"], - 1 - ) - when 'essence' - post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withessence", - [t['lootId'], 'CURRENCY_champion'], - 1 - ) - end - end - end - else - puts "You're missing #{needed_essence - owned_essence} Blue Essence needed to proceed. Skipping...".yellow - end - else - puts 'Found no upgradable set of Mastery Tokens.'.yellow - end - rescue => exception - handle_exception(exception, 'token upgrades') - end -end - -def handle_generic(name, type, recipe) - begin - player_loot = get_player_loot - disenchant_all = true - - loot_generic = player_loot.select { |l| l['type'] == type } - if count_loot_items(loot_generic) > 0 - puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue - - contains_unowned_items = false - loot_generic.each do |l| - if l['redeemableStatus'] != 'ALREADY_OWNED' - contains_unowned_items = true - end - end - - if contains_unowned_items - user_option = - user_input_check( - "Keep #{name} you don't own yet?\n".light_cyan + - '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + - "No\n".light_cyan + '[x] '.light_white + - "Exit to main menu\n".light_cyan + 'Option: ', - %w[y n x], - '[y|n|x]', - '' - ) - - case user_option - when 'x' - puts 'Action cancelled'.yellow - return - when 'y' - disenchant_all = false - loot_generic = - loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } - puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue - end - end - - if count_loot_items(loot_generic) > 0 - total_oe_value = 0 - loot_generic.each do |g| - total_oe_value += g['disenchantValue'] * g['count'] - end - - if loot_generic[0]['itemDesc'] == '' - loot_name_index = 'localizedName' - else - loot_name_index = 'itemDesc' - end - loot_generic = - loot_generic.sort_by do |l| - [l['redeemableStatus'], l[loot_name_index]] - end - - puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue - loot_generic.each do |l| - loot_value = l['disenchantValue'] * l['count'] - print pad("#{l['count']}x ", 5, false).light_black - print pad("#{l[loot_name_index]}", 30).light_white - print ' @ '.light_black - print pad("#{loot_value} OE", 8, false).light_black - if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' - print ' (not owned)'.yellow - end - puts - end - - if ($ans_y).include? user_input_check( - "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - $s_disenchanted += count_loot_items(loot_generic) - $s_orange_essence += total_oe_value - threads = - loot_generic.map do |g| - Thread.new { post_recipe(recipe, g['lootId'], g['count']) } - end - threads.each(&:join) - puts 'Done!'.green - end - else - puts "Found no owned #{name} to disenchant.".yellow - end - else - puts "Found no #{name} to disenchant.".yellow - end - rescue => exception - handle_exception(exception, name) - end -end - -def handle_skins - handle_generic('Skin Shards', 'SKIN_RENTAL', 'SKIN_RENTAL_disenchant') - handle_generic('Skin Permanents', 'SKIN', 'SKIN_disenchant') -end - -def handle_eternals - handle_generic( - 'Eternal Shards', - 'STATSTONE_SHARD', - 'STATSTONE_SHARD_DISENCHANT' - ) - handle_generic('Eternals', 'STATSTONE', 'STATSTONE_DISENCHANT') -end - -def handle_emotes - handle_generic('Emotes', 'EMOTE', 'EMOTE_disenchant') -end - -def handle_ward_skins - handle_generic( - 'Ward Skin Shards', - 'WARDSKIN_RENTAL', - 'WARDSKIN_RENTAL_disenchant' - ) - handle_generic('Ward Skin Permanents', 'WARDSKIN', 'WARDSKIN_disenchant') -end - -def handle_icons - handle_generic('Icons', 'SUMMONERICON', 'SUMMONERICON_disenchant') -end - -def handle_champions - begin - player_loot = get_player_loot - loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } - - loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } - if count_loot_items(loot_perms) > 0 - if ($ans_y).include? user_input_check( - 'Should we include champion permanents in this process?', - $ans_yn, - $ans_yn_d - ) - loot_shards = - player_loot.select do |l| - l['type'] == 'CHAMPION_RENTAL' || l['type'] == 'CHAMPION' - end - end - end - - if count_loot_items(loot_shards) > 0 - puts "Found #{count_loot_items(loot_shards)} champion shards.".light_blue - - loot_shards.each do |s| - s['count_keep'] = 0 - s['disenchant_note'] = '' - end - loot_shards_not_owned = - loot_shards.select { |s| s['redeemableStatus'] != 'ALREADY_OWNED' } - - if loot_shards_not_owned.length > 0 - if ($ans_y).include? user_input_check( - "Keep a shard for champions you don't own yet?", - $ans_yn, - $ans_yn_d - ) - loot_shards = handle_champions_owned(loot_shards) - end - else - puts "Found no shards of champions you don't own yet.".light_blue - end - - disenchant_modes = { - '1' => 'Disenchant all champion shards', - '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', - '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', - '4' => 'Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)', - '5' => 'Keep one shard of each champion regardless of mastery', - 'x' => 'Cancel', - } - - modes_string = '' - disenchant_modes.each do |k, v| - modes_string += "[#{k}] ".light_white - modes_string += "#{v}\n".light_cyan - end - - disenchant_shards_mode = - user_input_check( - "Okay, which option would you like to go by?\n" + modes_string + - 'Option: ', - disenchant_modes.keys, - '[1|2|3|4|5|x]', - '' - ) - unless disenchant_shards_mode == 'x' - case disenchant_shards_mode - when '1' - # no filtering needed -> done - when '2' - loot_shards = handle_champions_tokens(player_loot, loot_shards) - when '3' - loot_shards = handle_champions_mastery(loot_shards) - when '4' - loot_shards = handle_champions_mastery(loot_shards, true) - when '5' - loot_shards = handle_champions_collection(loot_shards) - end - - loot_shards = loot_shards.select { |l| l['count'] > 0 } - loot_shards = - loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } - - if count_loot_items(loot_shards) > 0 - puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue - loot_shards.each do |l| - loot_value = l['disenchantValue'] * l['count'] - print pad("#{l['count']}x ", 5, false).light_black - print pad("#{l['itemDesc']}", 15).light_white - print ' @ '.light_black - print pad("#{loot_value} BE", 8, false).light_black - if l['count_keep'] > 0 - puts " keeping #{l['count_keep']}".green - elsif l['disenchant_note'].length > 0 - puts " #{l['disenchant_note']}" - else - puts - end - end - - loot_shards = handle_champions_exceptions(loot_shards) - - total_be_value = 0 - loot_shards.each do |l| - total_be_value += l['disenchantValue'] * l['count'] - end - - if count_loot_items(loot_shards) > 0 - if $ans_y.include? user_input_check( - "Disenchant #{count_loot_items(loot_shards)} champion shards for #{total_be_value} Blue Essence?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - $s_blue_essence += total_be_value - $s_disenchanted += count_loot_items(loot_shards) - threads = - loot_shards.map do |s| - Thread.new do - post_recipe( - 'CHAMPION_RENTAL_disenchant', - s['lootId'], - s['count'] - ) - end - end - threads.each(&:join) - puts 'Done!'.green - end - else - puts 'All remaining champions have been excluded, skipping...'.yellow - end - else - puts "Job's already done: no champion shards left matching your selection.".green - end - else - puts 'Champion shard disenchanting canceled.'.yellow - end - else - puts 'Found no champion shards to disenchant.'.yellow - end - rescue => exception - handle_exception(exception, 'Champion Shards') - end -end - -def handle_champions_owned(loot_shards) - begin - loot_shards.each do |l| - unless l['redeemableStatus'] == 'ALREADY_OWNED' - l['count'] -= 1 - l['count_keep'] += 1 - end - end - return loot_shards.select { |l| l['count'] > 0 } - rescue => exception - handle_capsules(exception, 'Owned Champion Shards') - end -end - -def handle_champions_tokens(player_loot, loot_shards) - begin - token6_champion_ids = [] - token7_champion_ids = [] - - loot_mastery_tokens = - player_loot.select { |l| l['type'] == 'CHAMPION_TOKEN' } - - loot_mastery_tokens.each do |token| - if token['lootName'] = 'CHAMPION_TOKEN_6' - token6_champion_ids << token['refId'].to_i - elsif token['lootName'] = 'CHAMPION_TOKEN_7' - token7_champion_ids << token['refId'].to_i - end - end - - puts "Found #{token6_champion_ids.length + token7_champion_ids.length} champions with owned mastery tokens".light_black - - loot_shards = - loot_shards.each do |l| - if token6_champion_ids.include? l['storeItemId'] - l['count'] -= 2 - l['count_keep'] += 2 - elsif token7_champion_ids.include? l['storeItemId'] - l['count'] -= 1 - l['count_keep'] += 1 - end - end - return loot_shards - rescue => exception - handle_exception(exception, 'Champion Shards by Tokens') - end -end - -def handle_champions_mastery(loot_shards, keep_all = false) - begin - summoner = get_current_summoner - player_mastery = get_champion_mastery(summoner['summonerId']) - threshold_champion_ids = [] - mastery6_champion_ids = [] - mastery7_champion_ids = [] - - unless keep_all - level_threshold = - user_input_check( - 'Which mastery level should champions at least be for their shards to be kept?', - %w[1 2 3 4 5 6], - '[1..6]' - ) - else - level_threshold = '0' - end - level_threshold = level_threshold.to_i - - player_mastery.each do |m| - if m['championLevel'] == 7 - mastery7_champion_ids << m['championId'] - elsif m['championLevel'] == 6 - mastery6_champion_ids << m['championId'] - elsif (level_threshold..5).include? m['championLevel'] - threshold_champion_ids << m['championId'] - elsif keep_all - threshold_champion_ids << m['championId'] - end - end - - loot_shards.each do |l| - if mastery7_champion_ids.include? l['storeItemId'] - l['disenchant_note'] = 'at mastery 7'.light_black - elsif mastery6_champion_ids.include? l['storeItemId'] - l['count'] -= 1 - l['count_keep'] += 1 - elsif threshold_champion_ids.include? l['storeItemId'] - l['count'] -= 2 - l['count_keep'] += 2 - else - l['disenchant_note'] = 'below threshold'.yellow - end - end - - return loot_shards - rescue => exception - handle_exception(exception, 'Champion Shards by Mastery') - end -end - -def handle_champions_collection(loot_shards) - begin - loot_shards.each do |l| - l['count'] -= 1 - l['count_keep'] += 1 - end - - return loot_shards - rescue => exception - handle_exception(exception, 'Champion Shards for Collection') - end -end - -def handle_champions_exceptions(loot_shards) - begin - exclusions_str = '' - exclusions_done = false - exclusions_done_more = '' - exclusions_arr = [] - until exclusions_done - if ($ans_y).include? user_input_check( - "Would you like to add #{exclusions_done_more}exclusions?", - $ans_yn, - $ans_yn_d - ) - exclusions_str += - ',' + - ask( - 'Okay, which champions? '.light_cyan + - '(case-sensitive, comma-separated)'.light_white + - ': '.light_cyan - ) - - exclusions_done_more = 'more ' - - exclusions_arr = exclusions_str.split(/\s*,\s*/) - exclusions_matched = - loot_shards.select { |l| exclusions_arr.include? l['itemDesc'] } - print 'Exclusions recognized: '.green - exclusions_matched.each { |e| print e['itemDesc'].light_white + ' ' } - puts - else - exclusions_done = true - end - end - loot_shards = - loot_shards.select { |l| !exclusions_arr.include? l['itemDesc'] } - return loot_shards - rescue => exception - handle_exception(exception, 'Champion Shard Exceptions') - end -end - -def open_github - puts 'Opening GitHub repository at https://github.com/marvinscham/disenchanter/ in your browser...'.light_blue - Launchy.open('https://github.com/marvinscham/disenchanter/') -end - -def open_stats - puts 'Opening Global Stats at https://github.com/marvinscham/disenchanter/wiki/Stats in your browser...'.light_blue - Launchy.open('https://github.com/marvinscham/disenchanter/wiki/Stats') -end - -def handle_debug - done = false - things_todo = { - '1' => 'Write player_loot to file', - '2' => 'Write recipes of lootId to file', - '3' => 'Write loot info of lootId to file', - 'm' => 'Enable debug mode', - 'x' => 'Back to main menu', - } - things_done = [] - - until done - todo_string = '' - things_todo.each do |k, v| - todo_string += "[#{k}] ".light_white - unless things_done.include? k - todo_string += "#{v}\n".light_cyan - else - todo_string += "#{v} (done)\n".light_green - end - end - - todo = - user_input_check( - "\nWhat would you like to do?\n\n".light_cyan + todo_string + - 'Option: ', - things_todo.keys, - '', - '' - ) - things_done << todo - - puts $sep - puts - - puts "Option chosen: #{things_todo[todo]}".light_white - - case todo - when '1' - player_loot = get_player_loot - - File.open('disenchanter_loot.json', 'w') do |f| - f.write(player_loot.to_json) - end - - puts('Okay, written to disenchanter_loot.json.') - when '2' - loot_id = ask("Which lootId would you like the recipes for?\n".light_cyan) - - recipes = get_recipes_for_item loot_id - - File.open('disenchanter_recipes.json', 'w') do |f| - f.write(recipes.to_json) - end - - puts('Okay, written to disenchanter_recipes.json.') - when '3' - loot_id = ask("Which lootId would you like the info for?\n".light_cyan) - - loot_info = get_loot_info loot_id - - File.open('disenchanter_lootinfo.json', 'w') do |f| - f.write(loot_info.to_json) - end - - puts('Okay, written to disenchanter_lootinfo.json.') - when 'm' - $debug = true - puts 'Debug mode enabled.' - when 'x' - done = true - end - puts $sep - end -end - -def handle_stat_submission - if $actions > 0 - strlen = 15 - numlen = 7 - stats_string = "Your stats:\n".light_blue - stats_string += - pad('Actions', strlen) + pad($actions.to_s, numlen, false).light_white + - "\n" - stats_string += - pad('Disenchanted', strlen) + - pad($s_disenchanted.to_s, numlen, false).light_white + "\n" - stats_string += - pad('Opened', strlen) + pad($s_opened.to_s, numlen, false).light_white + - "\n" - stats_string += - pad('Crafted', strlen) + pad($s_crafted.to_s, numlen, false).light_white + - "\n" - stats_string += - pad('Redeemed', strlen) + - pad($s_redeemed.to_s, numlen, false).light_white + "\n" - stats_string += - pad('Blue Essence', strlen) + - pad($s_blue_essence.to_s, numlen, false).light_white + "\n" - stats_string += - pad('Orange Essence', strlen) + - pad($s_orange_essence.to_s, numlen, false).light_white + "\n" - - if ($ans_y).include? user_input_check( - "Would you like to contribute your (anonymous) stats to the global stats?\n".light_cyan + - stats_string + '[y|n]: ', - $ans_yn, - $ans_yn_d, - '' - ) - submit_stats( - $actions, - $s_disenchanted, - $s_opened, - $s_crafted, - $s_redeemed, - $s_blue_essence, - $s_orange_essence - ) - puts 'Thank you very much!'.light_green - end - end -end - -def submit_stats(a, d, o, c, r, be, oe) - begin - uri = URI('https://checksch.de/hook/disenchanter.php') - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Post.new(uri, "Content-Type": 'application/json') - - req.body = { a: a, d: d, o: o, c: c, r: r, be: be, oe: oe }.to_json - http.request(req) - rescue => exception - handle_exception(exception, 'stat submission') - end -end - -run diff --git a/disenchanter_up.rb b/disenchanter_up.rb deleted file mode 100644 index c03b971..0000000 --- a/disenchanter_up.rb +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require "net/https" -require "base64" -require "json" -require "colorize" -require "open-uri" - -puts "Grabbing latest version of Disenchanter...".light_blue - -def run - sep = - "____________________________________________________________".light_black - - if File.exist?("LeagueClient.exe") - # Doinb 400CS backwards compatibility hack - updater_processes = `tasklist | find /I /C "disenchanter_up.exe"` - if(updater_processes.to_i > 2) - puts "Backwards compatibility: killing Disenchanter..." - `taskkill /IM "disenchanter.exe" /F /T >nul 2>&1 && exit` - end - `tasklist|findstr "disenchanter.exe" >nul 2>&1 && echo Backwards compatibility: popping out into separate process... && start cmd.exe @cmd /k "disenchanter_up.exe" && exit` - sleep(1) - puts "Killing Disenchanter..." - `taskkill /IM "disenchanter.exe" /F /T >nul 2>&1` - - uri = - URI( - "https://api.github.com/repos/marvinscham/disenchanter/releases/latest" - ) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Get.new(uri, "Content-Type": "application/json") - res = http.request req - ans = JSON.parse(res.body) - - puts "Downloading Disenchanter #{ans["tag_name"]}".light_green - - `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans["tag_name"]}/disenchanter.exe -L -o disenchanter.exe` - puts sep - puts "Done downloading!".green - - pid = spawn("start cmd.exe @cmd /k \"disenchanter.exe\"") - Process.detach(pid) - puts "Exiting...".light_black - exit - else - puts "Not in League Client folder, skipping update...".yellow - end -end - -run diff --git a/scripts/build_main.sh b/scripts/build_main.sh new file mode 100644 index 0000000..0166dd0 --- /dev/null +++ b/scripts/build_main.sh @@ -0,0 +1,17 @@ +#!/bin/bash +mkdir -p build +touch ./build/.build.lockfile + +ocra src/main.rb \ + --gemfile ./Gemfile \ + --dll ruby_builtin_dlls/libffi-7.dll \ + --dll ruby_builtin_dlls/libssp-0.dll \ + --dll ruby_builtin_dlls/libgmp-10.dll \ + --dll ruby_builtin_dlls/libgcc_s_seh-1.dll \ + --dll ruby_builtin_dlls/libwinpthread-1.dll \ + --dll ruby_builtin_dlls/libssl-1_1-x64.dll \ + --dll ruby_builtin_dlls/libcrypto-1_1-x64.dll \ + --icon BE_icon.ico \ + --output ./build/disenchanter.exe + +rm ./build/.build.lockfile diff --git a/scripts/build_updater.sh b/scripts/build_updater.sh new file mode 100644 index 0000000..eb35e45 --- /dev/null +++ b/scripts/build_updater.sh @@ -0,0 +1,17 @@ +#!/bin/bash +mkdir -p build +touch ./build/.build.lockfile + +ocra src/updater.rb \ + --gemfile ./Gemfile \ + --dll ruby_builtin_dlls/libffi-7.dll \ + --dll ruby_builtin_dlls/libssp-0.dll \ + --dll ruby_builtin_dlls/libgmp-10.dll \ + --dll ruby_builtin_dlls/libgcc_s_seh-1.dll \ + --dll ruby_builtin_dlls/libwinpthread-1.dll \ + --dll ruby_builtin_dlls/libssl-1_1-x64.dll \ + --dll ruby_builtin_dlls/libcrypto-1_1-x64.dll \ + --icon BE_icon.ico \ + --output ./build/disenchanter_up.exe + +rm ./build/.build.lockfile diff --git a/src/main.rb b/src/main.rb new file mode 100644 index 0000000..07c8278 --- /dev/null +++ b/src/main.rb @@ -0,0 +1,166 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'net/https' +require 'base64' +require 'json' +require 'colorize' +require 'launchy' +require 'open-uri' +require 'rbconfig' + +require_relative 'modules/client_api' +require_relative 'modules/common_strings' +require_relative 'modules/debug' +require_relative 'modules/handlers' +require_relative 'modules/loot_metainfo' +require_relative 'modules/open_url' +require_relative 'modules/stat_submission' +require_relative 'modules/user_input' + +require_relative 'modules/update/checker' +require_relative 'modules/detect_client' + +def run + if File.exist?('./build/.build.lockfile') + puts 'Detected build environment, skipping execution...'.light_yellow + sleep 2 + exit + end + + set_globals + current_version = 'v1.6.0' + + puts 'Hi! :)'.light_green + puts "Running Disenchanter #{current_version} on port #{$port}".light_blue + puts 'You can exit this script at any point by pressing '.light_blue + + '[CTRL + C]'.light_white + '.'.light_blue + check_update(current_version) + puts separator + + summoner = get_current_summoner + if summoner['gameName'].nil? || summoner['gameName'].empty? + puts 'Could not grab summoner info. Try restarting your League Client.'.light_red + ask exit_string + exit 1 + end + puts "\nYou're logged in as #{summoner['gameName']} ##{summoner['tagLine']}.".light_blue + puts separator + puts "\nFeel free to try the options, no actions will be taken until you confirm a banner like this:".light_blue + puts 'CONFIRM: Perform this action? [y|n]'.light_magenta + puts separator + + done = false + things_todo = { + '1' => 'Materials', + '2' => 'Champions', + '3' => 'Skins', + #"4" => "Tacticians", + '5' => 'Eternals', + '6' => 'Emotes', + '7' => 'Ward Skins', + '8' => 'Icons', + 's' => 'Open Disenchanter Global Stats', + 'r' => 'Open GitHub repository', + 'd' => 'Debug Tools', + 'x' => 'Exit', + } + things_done = [] + + until done + todo_string = '' + things_todo.each do |k, v| + todo_string += "[#{k}] ".light_white + if things_done.include? k + todo_string += "#{v} (done)\n".light_green + else + todo_string += "#{v}\n".light_cyan + end + end + + todo = + user_input_check( + "\nWhat would you like to do? (Hint: do Materials first so you don't miss anything!)\n\n".light_cyan + + todo_string + 'Option: ', + things_todo.keys, + '', + '' + ) + things_done << todo + + puts separator + puts + + puts "Option chosen: #{things_todo[todo]}".light_white + + case todo + when '1' + handle_materials + when '2' + handle_champions + when '3' + handle_skins + # when "4" + # handle_tacticians + when '5' + handle_eternals + when '6' + handle_emotes + when '7' + handle_ward_skins + when '8' + handle_icons + when 's' + open_stats + when 'r' + open_github + when 'd' + handle_debug + when 'x' + done = true + end + refresh_loot + puts separator + end + + puts "That's it!".light_green + if $actions > 0 + puts "We saved you about #{$actions * 3} seconds of waiting for animations to finish.".light_green + puts separator + end + handle_stat_submission + puts 'See you next time :)'.light_green + ask exit_string +end + +def pad(str, len, right = true) + "%#{right ? '-' : ''}#{len}s" % str +end + +def set_globals + begin + $port, $token = grab_lockfile + rescue StandardError + puts 'Could not grab session!'.light_red + puts 'Make sure the script is in your League Client folder and that your Client is running.'.light_red + ask exit_string + exit 1 + end + $host = "https://127.0.0.1:#{$port}" + $debug = false + + $actions = 0 + $s_disenchanted = 0 + $s_opened = 0 + $s_crafted = 0 + $s_redeemed = 0 + $s_blue_essence = 0 + $s_orange_essence = 0 + + $ans_yn = %w[y yes n no] + $ans_y = %w[y yes] + $ans_n = %w[n no] + $ans_yn_d = '[y|n]' +end + +run diff --git a/src/modules/client_api.rb b/src/modules/client_api.rb new file mode 100644 index 0000000..b9775f6 --- /dev/null +++ b/src/modules/client_api.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +def create_client + Net::HTTP.start( + '127.0.0.1', + $port, + use_ssl: true, + verify_mode: OpenSSL::SSL::VERIFY_NONE, + ) { |http| yield(http) } +end + +def req_set_headers(req) + req['Content-Type'] = 'application/json' + req['Authorization'] = "Basic #{$token.chomp}" +end + +def request_get(path) + create_client do |http| + uri = URI("#{$host}/#{path}") + req = Net::HTTP::Get.new(uri) + req_set_headers(req) + res = http.request req + JSON.parse(res.body) + end +end + +def request_post(path, body) + create_client do |http| + uri = URI("#{$host}/#{path}") + req = Net::HTTP::Post.new(uri, "Content-Type": 'application/json') + req.body = body + req_set_headers(req) + res = http.request req + JSON.parse(res.body) + end +end + +def refresh_loot() + request_post('lol-loot/v1/refresh?force=true', '') +end + +def get_current_summoner() + request_get('lol-summoner/v1/current-summoner') +end + +def get_player_loot() + request_get('lol-loot/v1/player-loot') +end + +def get_champion_mastery(summoner_id) + request_get("lol-collections/v1/inventories/#{summoner_id}/champion-mastery") +end + +def get_loot_info(loot_id) + request_get("lol-loot/v1/player-loot/#{loot_id}") +end + +def get_recipes_for_item(loot_id) + request_get("lol-loot/v1/recipes/initial-item/#{loot_id}") +end + +def post_recipe(recipe, loot_ids, repeat) + $actions += repeat + + loot_id_string = "[\"" + Array(loot_ids).join("\", \"") + "\"]" + + op = + request_post( + "lol-loot/v1/recipes/#{recipe}/craft?repeat=#{repeat}", + loot_id_string + ) + + if $debug + File.open('disenchanter_post.json', 'w') { |f| f.write(op.to_json) } + puts('Okay, written to disenchanter_post.json.') + end + op +end diff --git a/src/modules/common_strings.rb b/src/modules/common_strings.rb new file mode 100644 index 0000000..e2133dd --- /dev/null +++ b/src/modules/common_strings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +def exit_string + 'Press Enter to exit.'.cyan +end + +def separator + '____________________________________________________________'.light_black +end diff --git a/src/modules/debug.rb b/src/modules/debug.rb new file mode 100644 index 0000000..7cc5bf1 --- /dev/null +++ b/src/modules/debug.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +def handle_debug + done = false + things_todo = { + '1' => 'Write player_loot to file', + '2' => 'Write recipes of lootId to file', + '3' => 'Write loot info of lootId to file', + 'm' => 'Enable debug mode', + 'x' => 'Back to main menu', + } + things_done = [] + + until done + todo_string = '' + things_todo.each do |k, v| + todo_string += "[#{k}] ".light_white + unless things_done.include? k + todo_string += "#{v}\n".light_cyan + else + todo_string += "#{v} (done)\n".light_green + end + end + + todo = + user_input_check( + "\nWhat would you like to do?\n\n".light_cyan + todo_string + + 'Option: ', + things_todo.keys, + '', + '' + ) + things_done << todo + + puts $sep + puts + + puts "Option chosen: #{things_todo[todo]}".light_white + + case todo + when '1' + player_loot = get_player_loot + + File.open('disenchanter_loot.json', 'w') do |f| + f.write(player_loot.to_json) + end + + puts('Okay, written to disenchanter_loot.json.') + when '2' + loot_id = ask("Which lootId would you like the recipes for?\n".light_cyan) + + recipes = get_recipes_for_item loot_id + + File.open('disenchanter_recipes.json', 'w') do |f| + f.write(recipes.to_json) + end + + puts('Okay, written to disenchanter_recipes.json.') + when '3' + loot_id = ask("Which lootId would you like the info for?\n".light_cyan) + + loot_info = get_loot_info loot_id + + File.open('disenchanter_lootinfo.json', 'w') do |f| + f.write(loot_info.to_json) + end + + puts('Okay, written to disenchanter_lootinfo.json.') + when 'm' + $debug = true + puts 'Debug mode enabled.' + when 'x' + done = true + end + puts $sep + end +end diff --git a/src/modules/detect_client.rb b/src/modules/detect_client.rb new file mode 100644 index 0000000..12a1e7e --- /dev/null +++ b/src/modules/detect_client.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +win_ident = /mswin|mingw|cygwin/ +require 'win32/registry' if RbConfig::CONFIG['host_os'] =~ win_ident +require 'win32/shortcut' if RbConfig::CONFIG['host_os'] =~ win_ident + +include Win32 if RbConfig::CONFIG['host_os'] =~ win_ident + +if RbConfig::CONFIG['host_os'] =~ win_ident + module Win32::Registry::Constants + KEY_WOW64_64KEY = 0x0100 + KEY_WOW64_32KEY = 0x0200 + end +end + +def grab_lockfile + is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) + if is_windows.zero? + lockfile = 'lockfile' + puts 'Trying to automatically get the path of the League Client'.green + begin + keyname = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live' + reg = Win32::Registry::HKEY_CURRENT_USER.open(keyname, + Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) + lockfile = "#{reg['InstallLocation']}/lockfile" + rescue + # do nothing + end + if lockfile == 'lockfile' + begin + sc = Shortcut.open('C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Riot Games\\League of Legends.lnk') + scpath = sc.path.split('\\') + path = scpath[0..-3].join('\\') + lockfile = "#{path}\\League of Legends\\lockfile" + rescue + # just keep going + end + end + end + + if lockfile == 'lockfile' + begin + contents = File.read("C:\\Riot Games\\League of Legends\\#{lockfile}") + rescue + puts 'Failed to automatically find your League Client path. ' \ + 'Please place the script directly in your League Client folder.'.light_red + contents = File.read(lockfile) + end + else + contents = File.read(lockfile) + end + + _leagueclient, _unk_port, port, password = contents.split(':') + token = Base64.encode64("riot:#{password.chomp}") + + [port, token] +end diff --git a/src/modules/handlers.rb b/src/modules/handlers.rb new file mode 100644 index 0000000..62e7997 --- /dev/null +++ b/src/modules/handlers.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require_relative 'handlers/generic' + +require_relative 'handlers/champions' +require_relative 'handlers/emotes' +require_relative 'handlers/eternals' +require_relative 'handlers/exception' +require_relative 'handlers/icons' +require_relative 'handlers/materials' +require_relative 'handlers/skins' +require_relative 'handlers/wards' diff --git a/src/modules/handlers/champions.rb b/src/modules/handlers/champions.rb new file mode 100644 index 0000000..a5880d0 --- /dev/null +++ b/src/modules/handlers/champions.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +require_relative 'champions/collection' +require_relative 'champions/exceptions' +require_relative 'champions/mastery' +require_relative 'champions/owned' +require_relative 'champions/tokens' + +def handle_champions + begin + player_loot = get_player_loot + loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } + + loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } + if count_loot_items(loot_perms) > 0 + if ($ans_y).include? user_input_check( + 'Should we include champion permanents in this process?', + $ans_yn, + $ans_yn_d + ) + loot_shards = + player_loot.select do |l| + l['type'] == 'CHAMPION_RENTAL' || l['type'] == 'CHAMPION' + end + end + end + + if count_loot_items(loot_shards) > 0 + puts "Found #{count_loot_items(loot_shards)} champion shards.".light_blue + + loot_shards.each do |s| + s['count_keep'] = 0 + s['disenchant_note'] = '' + end + loot_shards_not_owned = + loot_shards.select { |s| s['redeemableStatus'] != 'ALREADY_OWNED' } + + if loot_shards_not_owned.length > 0 + if ($ans_y).include? user_input_check( + "Keep a shard for champions you don't own yet?", + $ans_yn, + $ans_yn_d + ) + loot_shards = handle_champions_owned(loot_shards) + end + else + puts "Found no shards of champions you don't own yet.".light_blue + end + + disenchant_modes = { + '1' => 'Disenchant all champion shards', + '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', + '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', + '4' => 'Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)', + '5' => 'Keep one shard of each champion regardless of mastery', + 'x' => 'Cancel', + } + + modes_string = '' + disenchant_modes.each do |k, v| + modes_string += "[#{k}] ".light_white + modes_string += "#{v}\n".light_cyan + end + + disenchant_shards_mode = + user_input_check( + "Okay, which option would you like to go by?\n" + modes_string + + 'Option: ', + disenchant_modes.keys, + '[1|2|3|4|5|x]', + '' + ) + unless disenchant_shards_mode == 'x' + case disenchant_shards_mode + when '1' + # no filtering needed -> done + when '2' + loot_shards = handle_champions_tokens(player_loot, loot_shards) + when '3' + loot_shards = handle_champions_mastery(loot_shards) + when '4' + loot_shards = handle_champions_mastery(loot_shards, true) + when '5' + loot_shards = handle_champions_collection(loot_shards) + end + + loot_shards = loot_shards.select { |l| l['count'] > 0 } + loot_shards = + loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } + + if count_loot_items(loot_shards) > 0 + puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue + loot_shards.each do |l| + loot_value = l['disenchantValue'] * l['count'] + print pad("#{l['count']}x ", 5, false).light_black + print pad("#{l['itemDesc']}", 15).light_white + print ' @ '.light_black + print pad("#{loot_value} BE", 8, false).light_black + if l['count_keep'] > 0 + puts " keeping #{l['count_keep']}".green + elsif l['disenchant_note'].length > 0 + puts " #{l['disenchant_note']}" + else + puts + end + end + + loot_shards = handle_champions_exceptions(loot_shards) + + total_be_value = 0 + loot_shards.each do |l| + total_be_value += l['disenchantValue'] * l['count'] + end + + if count_loot_items(loot_shards) > 0 + if $ans_y.include? user_input_check( + "Disenchant #{count_loot_items(loot_shards)} champion shards for #{total_be_value} Blue Essence?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) + $s_blue_essence += total_be_value + $s_disenchanted += count_loot_items(loot_shards) + threads = + loot_shards.map do |s| + Thread.new do + post_recipe( + 'CHAMPION_RENTAL_disenchant', + s['lootId'], + s['count'] + ) + end + end + threads.each(&:join) + puts 'Done!'.green + end + else + puts 'All remaining champions have been excluded, skipping...'.yellow + end + else + puts "Job's already done: no champion shards left matching your selection.".green + end + else + puts 'Champion shard disenchanting canceled.'.yellow + end + else + puts 'Found no champion shards to disenchant.'.yellow + end + rescue => exception + handle_exception(exception, 'Champion Shards') + end +end diff --git a/src/modules/handlers/champions/collection.rb b/src/modules/handlers/champions/collection.rb new file mode 100644 index 0000000..df35e66 --- /dev/null +++ b/src/modules/handlers/champions/collection.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +def handle_champions_collection(loot_shards) + loot_shards.each do |l| + l['count'] -= 1 + l['count_keep'] += 1 + end + + loot_shards +rescue => e + handle_exception(e, 'Champion Shards for Collection') +end diff --git a/src/modules/handlers/champions/exceptions.rb b/src/modules/handlers/champions/exceptions.rb new file mode 100644 index 0000000..0b2c60f --- /dev/null +++ b/src/modules/handlers/champions/exceptions.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +def handle_champions_exceptions(loot_shards) + begin + exclusions_str = '' + exclusions_done = false + exclusions_done_more = '' + exclusions_arr = [] + until exclusions_done + if ($ans_y).include? user_input_check( + "Would you like to add #{exclusions_done_more}exclusions?", + $ans_yn, + $ans_yn_d + ) + exclusions_str += + ',' + + ask( + 'Okay, which champions? '.light_cyan + + '(case-sensitive, comma-separated)'.light_white + + ': '.light_cyan + ) + + exclusions_done_more = 'more ' + + exclusions_arr = exclusions_str.split(/\s*,\s*/) + exclusions_matched = + loot_shards.select { |l| exclusions_arr.include? l['itemDesc'] } + print 'Exclusions recognized: '.green + exclusions_matched.each { |e| print e['itemDesc'].light_white + ' ' } + puts + else + exclusions_done = true + end + end + loot_shards = + loot_shards.select { |l| !exclusions_arr.include? l['itemDesc'] } + return loot_shards + rescue => exception + handle_exception(exception, 'Champion Shard Exceptions') + end +end diff --git a/src/modules/handlers/champions/mastery.rb b/src/modules/handlers/champions/mastery.rb new file mode 100644 index 0000000..86cb83c --- /dev/null +++ b/src/modules/handlers/champions/mastery.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +def handle_champions_mastery(loot_shards, keep_all = false) + begin + summoner = get_current_summoner + player_mastery = get_champion_mastery(summoner['summonerId']) + threshold_champion_ids = [] + mastery6_champion_ids = [] + mastery7_champion_ids = [] + + unless keep_all + level_threshold = + user_input_check( + 'Which mastery level should champions at least be for their shards to be kept?', + %w[1 2 3 4 5 6], + '[1..6]' + ) + else + level_threshold = '0' + end + level_threshold = level_threshold.to_i + + player_mastery.each do |m| + if m['championLevel'] == 7 + mastery7_champion_ids << m['championId'] + elsif m['championLevel'] == 6 + mastery6_champion_ids << m['championId'] + elsif (level_threshold..5).include? m['championLevel'] + threshold_champion_ids << m['championId'] + elsif keep_all + threshold_champion_ids << m['championId'] + end + end + + loot_shards.each do |l| + if mastery7_champion_ids.include? l['storeItemId'] + l['disenchant_note'] = 'at mastery 7'.light_black + elsif mastery6_champion_ids.include? l['storeItemId'] + l['count'] -= 1 + l['count_keep'] += 1 + elsif threshold_champion_ids.include? l['storeItemId'] + l['count'] -= 2 + l['count_keep'] += 2 + else + l['disenchant_note'] = 'below threshold'.yellow + end + end + + return loot_shards + rescue => exception + handle_exception(exception, 'Champion Shards by Mastery') + end +end diff --git a/src/modules/handlers/champions/owned.rb b/src/modules/handlers/champions/owned.rb new file mode 100644 index 0000000..562fb53 --- /dev/null +++ b/src/modules/handlers/champions/owned.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +def handle_champions_owned(loot_shards) + begin + loot_shards.each do |l| + unless l['redeemableStatus'] == 'ALREADY_OWNED' + l['count'] -= 1 + l['count_keep'] += 1 + end + end + return loot_shards.select { |l| l['count'] > 0 } + rescue => exception + handle_capsules(exception, 'Owned Champion Shards') + end +end diff --git a/src/modules/handlers/champions/tokens.rb b/src/modules/handlers/champions/tokens.rb new file mode 100644 index 0000000..2d98049 --- /dev/null +++ b/src/modules/handlers/champions/tokens.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +def handle_champions_tokens(player_loot, loot_shards) + begin + token6_champion_ids = [] + token7_champion_ids = [] + + loot_mastery_tokens = + player_loot.select { |l| l['type'] == 'CHAMPION_TOKEN' } + + loot_mastery_tokens.each do |token| + if token['lootName'] = 'CHAMPION_TOKEN_6' + token6_champion_ids << token['refId'].to_i + elsif token['lootName'] = 'CHAMPION_TOKEN_7' + token7_champion_ids << token['refId'].to_i + end + end + + puts "Found #{token6_champion_ids.length + token7_champion_ids.length} champions with owned mastery tokens".light_black + + loot_shards = + loot_shards.each do |l| + if token6_champion_ids.include? l['storeItemId'] + l['count'] -= 2 + l['count_keep'] += 2 + elsif token7_champion_ids.include? l['storeItemId'] + l['count'] -= 1 + l['count_keep'] += 1 + end + end + return loot_shards + rescue => exception + handle_exception(exception, 'Champion Shards by Tokens') + end +end diff --git a/src/modules/handlers/emotes.rb b/src/modules/handlers/emotes.rb new file mode 100644 index 0000000..6d1fb1c --- /dev/null +++ b/src/modules/handlers/emotes.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +def handle_emotes + handle_generic('Emotes', 'EMOTE', 'EMOTE_disenchant') +end diff --git a/src/modules/handlers/eternals.rb b/src/modules/handlers/eternals.rb new file mode 100644 index 0000000..c931a48 --- /dev/null +++ b/src/modules/handlers/eternals.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +def handle_eternals + handle_generic( + 'Eternal Shards', + 'STATSTONE_SHARD', + 'STATSTONE_SHARD_DISENCHANT' + ) + handle_generic('Eternals', 'STATSTONE', 'STATSTONE_DISENCHANT') +end diff --git a/src/modules/handlers/exception.rb b/src/modules/handlers/exception.rb new file mode 100644 index 0000000..89cfc7d --- /dev/null +++ b/src/modules/handlers/exception.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +def handle_exception(exception, name) + puts "An error occurred while handling #{name}.".light_red + puts 'Please take a screenshot and create an issue at https://github.com/marvinscham/disenchanter/issues/new'.light_red + puts "If you don't have a GitHub account, send it to dev@marvinscham.de".light_red + puts exception + puts 'Skipping this step...'.yellow +end diff --git a/src/modules/handlers/generic.rb b/src/modules/handlers/generic.rb new file mode 100644 index 0000000..bd95aa4 --- /dev/null +++ b/src/modules/handlers/generic.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +def handle_generic(name, type, recipe) + begin + player_loot = get_player_loot + disenchant_all = true + + loot_generic = player_loot.select { |l| l['type'] == type } + if count_loot_items(loot_generic) > 0 + puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue + + contains_unowned_items = false + loot_generic.each do |l| + if l['redeemableStatus'] != 'ALREADY_OWNED' + contains_unowned_items = true + end + end + + if contains_unowned_items + user_option = + user_input_check( + "Keep #{name} you don't own yet?\n".light_cyan + + '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + + "No\n".light_cyan + '[x] '.light_white + + "Exit to main menu\n".light_cyan + 'Option: ', + %w[y n x], + '[y|n|x]', + '' + ) + + case user_option + when 'x' + puts 'Action cancelled'.yellow + return + when 'y' + disenchant_all = false + loot_generic = + loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } + puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue + end + end + + if count_loot_items(loot_generic) > 0 + total_oe_value = 0 + loot_generic.each do |g| + total_oe_value += g['disenchantValue'] * g['count'] + end + + if loot_generic[0]['itemDesc'] == '' + loot_name_index = 'localizedName' + else + loot_name_index = 'itemDesc' + end + loot_generic = + loot_generic.sort_by do |l| + [l['redeemableStatus'], l[loot_name_index]] + end + + puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue + loot_generic.each do |l| + loot_value = l['disenchantValue'] * l['count'] + print pad("#{l['count']}x ", 5, false).light_black + print pad("#{l[loot_name_index]}", 30).light_white + print ' @ '.light_black + print pad("#{loot_value} OE", 8, false).light_black + if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' + print ' (not owned)'.yellow + end + puts + end + + if ($ans_y).include? user_input_check( + "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) + $s_disenchanted += count_loot_items(loot_generic) + $s_orange_essence += total_oe_value + threads = + loot_generic.map do |g| + Thread.new { post_recipe(recipe, g['lootId'], g['count']) } + end + threads.each(&:join) + puts 'Done!'.green + end + else + puts "Found no owned #{name} to disenchant.".yellow + end + else + puts "Found no #{name} to disenchant.".yellow + end + rescue => exception + handle_exception(exception, name) + end +end diff --git a/src/modules/handlers/icons.rb b/src/modules/handlers/icons.rb new file mode 100644 index 0000000..2aab83d --- /dev/null +++ b/src/modules/handlers/icons.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +def handle_icons + handle_generic('Icons', 'SUMMONERICON', 'SUMMONERICON_disenchant') +end diff --git a/src/modules/handlers/materials.rb b/src/modules/handlers/materials.rb new file mode 100644 index 0000000..2641b90 --- /dev/null +++ b/src/modules/handlers/materials.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require_relative 'materials/capsules' +require_relative 'materials/key_fragments' +require_relative 'materials/mastery_tokens' +require_relative 'materials/mythic_essence' + +def handle_materials + done = false + things_todo = { + '1' => 'Mythic Essence', + '2' => 'Key Fragments', + '3' => 'Capsules', + '4' => 'Mastery Tokens', + 'x' => 'Back to main menu', + } + things_done = [] + + until done + todo_string = '' + things_todo.each do |k, v| + todo_string += "[#{k}] ".light_white + unless things_done.include? k + todo_string += "#{v}\n".light_cyan + else + todo_string += "#{v} (done)\n".light_green + end + end + + todo = + user_input_check( + "\nWhat would you like to do?\n\n".light_cyan + todo_string + + 'Option: ', + things_todo.keys, + '', + '' + ) + things_done << todo + + puts $sep + puts + + puts "Option chosen: #{things_todo[todo]}".light_white + + case todo + when '1' + handle_mythic_essence + when '2' + handle_key_fragments + when '3' + handle_capsules + when '4' + handle_mastery_tokens + when 'x' + done = true + end + puts $sep + end +end diff --git a/src/modules/handlers/materials/capsules.rb b/src/modules/handlers/materials/capsules.rb new file mode 100644 index 0000000..6c41d8d --- /dev/null +++ b/src/modules/handlers/materials/capsules.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +def handle_capsules + begin + player_loot = get_player_loot + + loot_capsules = + player_loot.select { |l| l['lootName'].start_with?('CHEST_') } + loot_capsules.each do |c| + recipes = get_recipes_for_item(c['lootId']) + if recipes[0]['slots'].length > 1 || !recipes[0]['type'] == 'OPEN' + c['needs_key'] = true + else + c['needs_key'] = false + end + end + loot_capsules = loot_capsules.select { |c| c['needs_key'] == false } + + if count_loot_items(loot_capsules) > 0 + puts "Found #{count_loot_items(loot_capsules)} capsules:".light_blue + loot_capsules.each do |c| + puts "#{c['count']}x ".light_black + + "#{get_chest_name(c['lootId'])}".light_white + end + + if ($ans_y).include? user_input_check( + "Open #{count_loot_items(loot_capsules)} (keyless) capsules?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) + $s_opened += count_loot_items(loot_capsules) + threads = + loot_capsules.map do |c| + Thread.new do + res = post_recipe(c['lootId'] + '_OPEN', c['lootId'], c['count']) + res['added'].each do |r| + if r['playerLoot']['lootId'] == 'CURRENCY_champion' + $s_blue_essence += r['deltaCount'] + end + end + end + end + threads.each(&:join) + puts 'Done!'.green + end + else + puts 'Found no keyless capsules to open.'.yellow + end + rescue => exception + handle_exception(exception, 'Capsules') + end +end diff --git a/src/modules/handlers/materials/key_fragments.rb b/src/modules/handlers/materials/key_fragments.rb new file mode 100644 index 0000000..ee95fa1 --- /dev/null +++ b/src/modules/handlers/materials/key_fragments.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +def handle_key_fragments + begin + player_loot = get_player_loot + + loot_keys = + player_loot.select { |l| l['lootId'] == 'MATERIAL_key_fragment' } + if count_loot_items(loot_keys) >= 3 + puts "Found #{count_loot_items(loot_keys)} key fragments.".light_blue + if ($ans_y).include? user_input_check( + "Craft #{(count_loot_items(loot_keys) / 3).floor} keys from #{count_loot_items(loot_keys)} key fragments?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) + $s_crafted += (count_loot_items(loot_keys) / 3).floor + post_recipe( + 'MATERIAL_key_fragment_forge', + 'MATERIAL_key_fragment', + (count_loot_items(loot_keys) / 3).floor + ) + puts 'Done!'.green + end + else + puts 'Found less than 3 key fragments.'.yellow + end + rescue => exception + handle_exception(exception, 'Key Fragments') + end +end diff --git a/src/modules/handlers/materials/mastery_tokens.rb b/src/modules/handlers/materials/mastery_tokens.rb new file mode 100644 index 0000000..afc633c --- /dev/null +++ b/src/modules/handlers/materials/mastery_tokens.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +def handle_mastery_tokens + begin + player_loot = get_player_loot + loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } + loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } + + recipes6 = get_recipes_for_item('CHAMPION_TOKEN_6-1') + recipes7 = get_recipes_for_item('CHAMPION_TOKEN_7-1') + recipe6_cost = + recipes6.select do |r| + r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' + end + recipe6_cost = recipe6_cost[0]['slots'][1]['quantity'] + recipe7_cost = + recipes7.select do |r| + r['recipeName'] == 'CHAMPION_TOKEN_7_redeem_withessence' + end + recipe7_cost = recipe7_cost[0]['slots'][1]['quantity'] + + loot_overall_tokens = + player_loot.select do |l| + (l['lootName'] == 'CHAMPION_TOKEN_6') || + (l['lootName'] == 'CHAMPION_TOKEN_7') + end + + puts "Found #{count_loot_items(loot_overall_tokens)} Mastery Tokens.".light_blue + + loot_mastery_tokens = + player_loot.select do |l| + (l['lootName'] == 'CHAMPION_TOKEN_6' && l['count'] == 2) || + (l['lootName'] == 'CHAMPION_TOKEN_7' && l['count'] == 3) + end + + if loot_mastery_tokens.count > 0 + loot_mastery_tokens = + loot_mastery_tokens.sort_by { |l| [l['lootName'], l['itemDesc']] } + puts "We could upgrade the following champions:\n".light_blue + needed_shards = 0 + needed_perms = 0 + needed_essence = 0 + + loot_mastery_tokens.each do |t| + ref_shard = + loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } + ref_perm = + loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } + + print pad(t['itemDesc'], 15, false).light_white + print ' to Mastery Level '.light_black + print "#{(t['lootName'])[-1]}".light_white + print ' using '.light_black + if !ref_shard.empty? && ref_shard[0]['count'] > 0 + print 'a champion shard.'.green + needed_shards += 1 + t['upgrade_type'] = 'shard' + elsif !ref_perm.empty? && ref_shard[0]['count'] > 0 + print 'a champion permanent.'.green + needed_perms += 1 + t['upgrade_type'] = 'permanent' + else + recipe_cost = (t['lootName'])[-1] == '6' ? recipe6_cost : recipe7_cost + print "#{recipe_cost} Blue Essence.".yellow + needed_essence += recipe_cost + t['upgrade_type'] = 'essence' + end + puts + end + puts + + owned_essence = + player_loot.select { |l| l['lootId'] == 'CURRENCY_champion' } + owned_essence = owned_essence[0]['count'] + if (owned_essence > needed_essence) + question_string = + "Upgrade #{loot_mastery_tokens.count} champions using " + question_string += "#{needed_shards} Shards, " if needed_shards > 0 + question_string += "#{needed_perms} Permanents, " if needed_perms > 0 + question_string += + "#{needed_essence} Blue Essence, " if needed_essence > 0 + question_string = question_string.delete_suffix(', ') + question_string += '?' + + if $ans_y.include? user_input_check( + question_string, + $ans_yn, + $ans_yn_d, + 'confirm' + ) + loot_mastery_tokens.each do |t| + $s_redeemed += 1 + target_level = (t['lootName'])[-1] + case t['upgrade_type'] + when 'shard' + post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withshard", + [t['lootId'], "CHAMPION_RENTAL_#{t['refId']}"], + 1 + ) + when 'permanent' + post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withpermanent", + [t['lootId'], "CHAMPION_#{t['refId']}"], + 1 + ) + when 'essence' + post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withessence", + [t['lootId'], 'CURRENCY_champion'], + 1 + ) + end + end + end + else + puts "You're missing #{needed_essence - owned_essence} Blue Essence needed to proceed. Skipping...".yellow + end + else + puts 'Found no upgradable set of Mastery Tokens.'.yellow + end + rescue => exception + handle_exception(exception, 'token upgrades') + end +end diff --git a/src/modules/handlers/materials/mythic_essence.rb b/src/modules/handlers/materials/mythic_essence.rb new file mode 100644 index 0000000..6a74a63 --- /dev/null +++ b/src/modules/handlers/materials/mythic_essence.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +def handle_mythic_essence + begin + player_loot = get_player_loot + mythic_loot_id = 'CURRENCY_mythic' + + loot_essence = player_loot.select { |l| l['lootId'] == mythic_loot_id } + loot_essence = loot_essence[0] + if !loot_essence.nil? && loot_essence['count'] > 0 + puts "Found #{loot_essence['count']} Mythic Essence.".light_blue + craft_mythic_type_names = [ + 'Blue Essence', + 'Orange Essence', + 'Random Skin Shards', + ] + + craft_mythic_type = + user_input_check( + "Okay, what would you like to craft?\n" + + "[1] #{craft_mythic_type_names[0]}\n" + + "[2] #{craft_mythic_type_names[1]}\n" + + "[3] #{craft_mythic_type_names[2]}\n" + "[x] Cancel\n", + %w[1 2 3 x], + '[1|2|3|x]' + ) + + unless craft_mythic_type == 'x' + case craft_mythic_type + # Blue Essence, Orange Essence, Random Skin Shard + when '1' + recipe_target = 'CURRENCY_champion' + when '2' + recipe_target = 'CURRENCY_cosmetic' + when '3' + recipe_target = 'CHEST_291' + end + + recipes = get_recipes_for_item(mythic_loot_id) + recipes = + recipes.select { |r| r['outputs'][0]['lootName'] == recipe_target } + unless recipes.length == 0 + recipe = recipes[0] + + puts "Recipe found: #{recipe['contextMenuText']} for #{recipe['slots'][0]['quantity']} Mythic Essence".light_blue + + craft_mythic_amount = + user_input_check( + "Alright, how much Mythic Essence should we use to craft #{craft_mythic_type_names[craft_mythic_type.to_i - 1]}?", + (1..loot_essence['count'].to_i) + .to_a + .append('all') + .map! { |n| n.to_s }, + "[1..#{loot_essence['count']}|all]" + ) + + if craft_mythic_amount == 'all' + craft_mythic_amount = loot_essence['count'] + end + craft_mythic_amount = craft_mythic_amount.to_i + + could_craft = + (craft_mythic_amount / recipe['slots'][0]['quantity']).floor + unless could_craft < 1 + if ($ans_y).include? user_input_check( + "Craft #{could_craft * recipe['outputs'][0]['quantity']} " + + "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]} from " + + "#{(craft_mythic_amount / recipe['slots'][0]['quantity']).floor * recipe['slots'][0]['quantity']} Mythic Essence?", + $ans_yn, + $ans_yn_d, + 'confirm' + ) + case craft_mythic_type + when '1' + $s_blue_essence += + could_craft * recipe['outputs'][0]['quantity'] + when '2' + $s_orange_essence += + could_craft * recipe['outputs'][0]['quantity'] + end + $s_crafted += could_craft + + post_recipe( + recipe['recipeName'], + mythic_loot_id, + (craft_mythic_amount / recipe['slots'][0]['quantity']).floor + ) + puts 'Done!'.green + end + else + puts 'Not enough Mythic Essence for that recipe.'.yellow + end + else + puts "Recipes for #{craft_mythic_type_names[craft_mythic_type.to_i - 1]} seem to be unavailable.".yellow + end + else + puts 'Mythic crafting canceled.'.yellow + end + else + puts 'Found no Mythic Essence to use.'.yellow + end + rescue => exception + handle_exception(exception, 'Mythic Essence') + end +end diff --git a/src/modules/handlers/skins.rb b/src/modules/handlers/skins.rb new file mode 100644 index 0000000..ea38667 --- /dev/null +++ b/src/modules/handlers/skins.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +def handle_skins + handle_generic('Skin Shards', 'SKIN_RENTAL', 'SKIN_RENTAL_disenchant') + handle_generic('Skin Permanents', 'SKIN', 'SKIN_disenchant') +end diff --git a/src/modules/handlers/wards.rb b/src/modules/handlers/wards.rb new file mode 100644 index 0000000..48ff07a --- /dev/null +++ b/src/modules/handlers/wards.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +def handle_ward_skins + handle_generic( + 'Ward Skin Shards', + 'WARDSKIN_RENTAL', + 'WARDSKIN_RENTAL_disenchant' + ) + handle_generic('Ward Skin Permanents', 'WARDSKIN', 'WARDSKIN_disenchant') +end diff --git a/src/modules/loot_metainfo.rb b/src/modules/loot_metainfo.rb new file mode 100644 index 0000000..61e4fe5 --- /dev/null +++ b/src/modules/loot_metainfo.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +def count_loot_items(loot_items) + count = 0 + unless loot_items.nil? || loot_items.empty? + loot_items.each { |loot| count += loot['count'] } + end + count +end + +def get_chest_name(loot_id) + chest_info = get_loot_info(loot_id) + return chest_info['localizedName'] if !chest_info['localizedName'].empty? + + catalogue = { + 'CHEST_128' => 'Champion Capsule', + 'CHEST_129' => 'Glorious Champion Capsule', + 'CHEST_210' => 'Honor Level 4 Orb', + 'CHEST_211' => 'Honor Level 5 Orb', + } + + return catalogue[loot_id] if catalogue.key?(loot_id) + + return loot_id +end diff --git a/src/modules/open_url.rb b/src/modules/open_url.rb new file mode 100644 index 0000000..bed1eaf --- /dev/null +++ b/src/modules/open_url.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +def open_github + puts 'Opening GitHub repository at https://github.com/marvinscham/disenchanter/ in your browser...'.light_blue + Launchy.open('https://github.com/marvinscham/disenchanter/') +end + +def open_stats + puts 'Opening Global Stats at https://github.com/marvinscham/disenchanter/wiki/Stats in your browser...'.light_blue + Launchy.open('https://github.com/marvinscham/disenchanter/wiki/Stats') +end diff --git a/src/modules/stat_submission.rb b/src/modules/stat_submission.rb new file mode 100644 index 0000000..fff0c9f --- /dev/null +++ b/src/modules/stat_submission.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +def submit_stats(a, d, o, c, r, be, oe) + begin + uri = URI('https://checksch.de/hook/disenchanter.php') + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + req = Net::HTTP::Post.new(uri, "Content-Type": 'application/json') + + req.body = { a: a, d: d, o: o, c: c, r: r, be: be, oe: oe }.to_json + http.request(req) + rescue => exception + handle_exception(exception, 'stat submission') + end +end + +def handle_stat_submission + if $actions > 0 + strlen = 15 + numlen = 7 + stats_string = "Your stats:\n".light_blue + stats_string += + pad('Actions', strlen) + pad($actions.to_s, numlen, false).light_white + + "\n" + stats_string += + pad('Disenchanted', strlen) + + pad($s_disenchanted.to_s, numlen, false).light_white + "\n" + stats_string += + pad('Opened', strlen) + pad($s_opened.to_s, numlen, false).light_white + + "\n" + stats_string += + pad('Crafted', strlen) + pad($s_crafted.to_s, numlen, false).light_white + + "\n" + stats_string += + pad('Redeemed', strlen) + + pad($s_redeemed.to_s, numlen, false).light_white + "\n" + stats_string += + pad('Blue Essence', strlen) + + pad($s_blue_essence.to_s, numlen, false).light_white + "\n" + stats_string += + pad('Orange Essence', strlen) + + pad($s_orange_essence.to_s, numlen, false).light_white + "\n" + + if ($ans_y).include? user_input_check( + "Would you like to contribute your (anonymous) stats to the global stats?\n".light_cyan + + stats_string + '[y|n]: ', + $ans_yn, + $ans_yn_d, + '' + ) + submit_stats( + $actions, + $s_disenchanted, + $s_opened, + $s_crafted, + $s_redeemed, + $s_blue_essence, + $s_orange_essence + ) + puts 'Thank you very much!'.light_green + end + end +end diff --git a/src/modules/update/backwards_compat.rb b/src/modules/update/backwards_compat.rb new file mode 100644 index 0000000..93d0a76 --- /dev/null +++ b/src/modules/update/backwards_compat.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +def backwards_compat + # Doinb 400CS backwards compatibility hack + updater_processes = `tasklist | find /I /C "disenchanter_up.exe"` + + kill_disenchanter if updater_processes.to_i > 2 + + `tasklist|findstr "disenchanter.exe" >nul 2>&1 \ + && echo Backwards compatibility: popping out into separate process... \ + && start cmd.exe @cmd /k "disenchanter_up.exe" \ + && exit` + sleep(1) + kill_disenchanter +end + +def kill_disenchanter + puts 'Killing Disenchanter...' + `taskkill /IM "disenchanter.exe" /F /T >nul 2>&1 && exit` +end diff --git a/src/modules/update/checker.rb b/src/modules/update/checker.rb new file mode 100644 index 0000000..5d1c3a3 --- /dev/null +++ b/src/modules/update/checker.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +def check_update(version_local) + begin + uri = + URI( + 'https://api.github.com/repos/marvinscham/disenchanter/releases/latest' + ) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + req = Net::HTTP::Get.new(uri, "Content-Type": 'application/json') + res = http.request req + ans = JSON.parse(res.body) + + version_local = + Gem::Version.new(version_local.delete_prefix('v').delete_suffix('-beta')) + version_remote = + Gem::Version.new( + ans['tag_name'].delete_prefix('v').delete_suffix('-beta') + ) + + if version_remote > version_local + puts "New version #{ans['tag_name']} available!".light_yellow + if ($ans_y).include? user_input_check( + 'Would you like to download the new version now?', + $ans_yn, + $ans_yn_d + ) + `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans['tag_name']}/disenchanter_up.exe -L -o disenchanter_up.exe` + puts 'Done downloading!'.green + + pid = spawn("start cmd.exe @cmd /k \"disenchanter_up.exe\"") + Process.detach(pid) + puts 'Exiting...'.light_black + exit + end + elsif version_local > version_remote + puts 'Welcome to the future!'.light_magenta + puts "Latest remote version: v#{version_remote}".light_blue + else + puts "You're up to date!".green + end + rescue => exception + handle_exception(exception, 'self update') + end +end diff --git a/src/modules/update/download.rb b/src/modules/update/download.rb new file mode 100644 index 0000000..f8a99ae --- /dev/null +++ b/src/modules/update/download.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +def download_new_version + ans = fetch_newest_release + + puts "Downloading Disenchanter #{ans['tag_name']}".light_green + `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans['tag_name']}/disenchanter.exe \ + -L -o disenchanter.exe` + + puts '____________________________________________________________'.light_black + puts 'Done downloading!'.green +end + +def fetch_newest_release + uri = URI('https://api.github.com/repos/marvinscham/disenchanter/releases/latest') + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + req = Net::HTTP::Get.new(uri, 'Content-Type': 'application/json') + res = http.request req + JSON.parse(res.body) +end diff --git a/src/modules/user_input.rb b/src/modules/user_input.rb new file mode 100644 index 0000000..c37b20e --- /dev/null +++ b/src/modules/user_input.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +def ask(q) + print(q) + q = gets + q.chomp +end + +def user_input_check(question, answers, answerdisplay, color_preset = 'default') + input = '' + + case color_preset + when 'confirm' + question = + "CONFIRM: #{question} ".light_magenta + "#{answerdisplay}".light_white + + ': '.light_magenta + when 'default' + question = + "#{question} ".light_cyan + "#{answerdisplay}".light_white + + ': '.light_cyan + end + + until (answers).include? input + input = ask question + unless (answers).include? input + puts 'Invalid answer, options: '.light_red + + "#{answerdisplay}".light_white + end + end + + input +end diff --git a/src/updater.rb b/src/updater.rb new file mode 100644 index 0000000..995b895 --- /dev/null +++ b/src/updater.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'net/https' +require 'base64' +require 'json' +require 'colorize' +require 'open-uri' +require_relative 'modules/update/backwards_compat' +require_relative 'modules/update/download' + +puts 'Grabbing latest version of Disenchanter...'.light_blue + +def run + if File.exist?('./build/.build.lockfile') + puts 'Detected build environment, skipping execution...'.light_yellow + sleep 2 + exit + end + + backwards_compat + download_new_version + + pid = spawn('start cmd.exe @cmd /k "disenchanter.exe"') + Process.detach(pid) + puts 'Exiting...'.light_black +end + +run From 77383bdca235c2aef4b8b6725fe086e3ea50a24f Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 25 Jan 2024 03:04:50 +0100 Subject: [PATCH 21/63] Pulled client and stat tracker into separate classes --- .rufo | 3 +- src/class/client.rb | 107 +++++++++++++++++++++++++++++++++ src/class/statTracker.rb | 42 +++++++++++++ src/main.rb | 36 +++-------- src/modules/client_api.rb | 78 ------------------------ src/modules/detect_client.rb | 22 ++++--- src/modules/stat_submission.rb | 2 +- 7 files changed, 175 insertions(+), 115 deletions(-) create mode 100644 src/class/client.rb create mode 100644 src/class/statTracker.rb delete mode 100644 src/modules/client_api.rb diff --git a/.rufo b/.rufo index c0f958c..36dfe81 100644 --- a/.rufo +++ b/.rufo @@ -1 +1,2 @@ -quote_style :single \ No newline at end of file +quote_style :single +trailing_commas false diff --git a/src/class/client.rb b/src/class/client.rb new file mode 100644 index 0000000..f26f055 --- /dev/null +++ b/src/class/client.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require_relative '../modules/detect_client' + +# Holds port and token info +class Client + def initialize(stat_tracker) + begin + @port, @token = grab_lockfile + rescue StandardError + puts 'Could not grab session!'.light_red + puts 'Make sure the script is in your League Client folder and that your Client is running.'.light_red + ask exit_string + exit 1 + end + @stat_tracker = stat_tracker + @debug = false + end + + def host + "https://127.0.0.1:#{@port}" + end + + def auth + "Basic #{@token.chomp}" + end + + def create_client + Net::HTTP.start( + '127.0.0.1', + @port, + use_ssl: true, + verify_mode: OpenSSL::SSL::VERIFY_NONE + ) { |http| yield(http) } + end + + def req_set_headers(req) + req['Content-Type'] = 'application/json' + req['Authorization'] = auth + end + + def request_get(path) + create_client do |http| + uri = URI("#{host}/#{path}") + req = Net::HTTP::Get.new(uri) + req_set_headers(req) + res = http.request req + JSON.parse(res.body) + end + end + + def request_post(path, body) + create_client do |http| + uri = URI("#{host}/#{path}") + req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') + req.body = body + req_set_headers(req) + res = http.request req + JSON.parse(res.body) + end + end + + def refresh_loot + request_post('lol-loot/v1/refresh?force=true', '') + end + + def req_get_current_summoner + request_get('lol-summoner/v1/current-summoner') + end + + def req_get_player_loot + request_get('lol-loot/v1/player-loot') + end + + def req_get_champion_mastery(summoner_id) + request_get("lol-collections/v1/inventories/#{summoner_id}/champion-mastery") + end + + def req_get_loot_info(loot_id) + request_get("lol-loot/v1/player-loot/#{loot_id}") + end + + def req_get_recipes_for_item(loot_id) + request_get("lol-loot/v1/recipes/initial-item/#{loot_id}") + end + + def req_post_recipe(recipe, loot_ids, repeat) + @stat_tracker.add_actions(repeat) + + loot_id_string = "[\"#{Array(loot_ids).join('", "')}\"]" + + op = + request_post( + "lol-loot/v1/recipes/#{recipe}/craft?repeat=#{repeat}", + loot_id_string + ) + handle_post_debug + op + end + + def handle_post_debug + return unless @debug + + File.write('disenchanter_post.json', op.to_json) + puts('Okay, written to disenchanter_post.json.') + end +end diff --git a/src/class/statTracker.rb b/src/class/statTracker.rb new file mode 100644 index 0000000..66eef69 --- /dev/null +++ b/src/class/statTracker.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# Tracks usage stats for later optional submission +class StatTracker + def initialize + @actions = 0 + @blue_essence = 0 + @orange_essence = 0 + @disenchanted = 0 + @crafted = 0 + @redeemed = 0 + @opened = 0 + end + + def add_blue_essence(count) + @blue_essence += count + end + + def add_orange_essence(count) + @orange_essence += count + end + + def add_disenchanted(count) + @disenchanted += count + end + + def add_actions(count) + @actions += count + end + + def add_crafted(count) + @crafted += count + end + + def add_redeemed(count) + @redeemed += count + end + + def add_opened(count) + @opened += count + end +end diff --git a/src/main.rb b/src/main.rb index 07c8278..3936c39 100644 --- a/src/main.rb +++ b/src/main.rb @@ -9,7 +9,9 @@ require 'open-uri' require 'rbconfig' -require_relative 'modules/client_api' +require_relative 'class/client' +require_relative 'class/statTracker' + require_relative 'modules/common_strings' require_relative 'modules/debug' require_relative 'modules/handlers' @@ -19,7 +21,6 @@ require_relative 'modules/user_input' require_relative 'modules/update/checker' -require_relative 'modules/detect_client' def run if File.exist?('./build/.build.lockfile') @@ -30,15 +31,17 @@ def run set_globals current_version = 'v1.6.0' + stat_tracker = StatTracker.new + client = Client.new(stat_tracker) puts 'Hi! :)'.light_green - puts "Running Disenchanter #{current_version} on port #{$port}".light_blue - puts 'You can exit this script at any point by pressing '.light_blue + - '[CTRL + C]'.light_white + '.'.light_blue + puts "Running Disenchanter #{current_version}".light_blue check_update(current_version) + print 'You can exit this script at any point by pressing '.light_blue + puts '[CTRL + C]'.light_white + '.'.light_blue puts separator - summoner = get_current_summoner + summoner = client.req_get_current_summoner if summoner['gameName'].nil? || summoner['gameName'].empty? puts 'Could not grab summoner info. Try restarting your League Client.'.light_red ask exit_string @@ -63,7 +66,7 @@ def run 's' => 'Open Disenchanter Global Stats', 'r' => 'Open GitHub repository', 'd' => 'Debug Tools', - 'x' => 'Exit', + 'x' => 'Exit' } things_done = [] @@ -138,25 +141,6 @@ def pad(str, len, right = true) end def set_globals - begin - $port, $token = grab_lockfile - rescue StandardError - puts 'Could not grab session!'.light_red - puts 'Make sure the script is in your League Client folder and that your Client is running.'.light_red - ask exit_string - exit 1 - end - $host = "https://127.0.0.1:#{$port}" - $debug = false - - $actions = 0 - $s_disenchanted = 0 - $s_opened = 0 - $s_crafted = 0 - $s_redeemed = 0 - $s_blue_essence = 0 - $s_orange_essence = 0 - $ans_yn = %w[y yes n no] $ans_y = %w[y yes] $ans_n = %w[n no] diff --git a/src/modules/client_api.rb b/src/modules/client_api.rb deleted file mode 100644 index b9775f6..0000000 --- a/src/modules/client_api.rb +++ /dev/null @@ -1,78 +0,0 @@ -# frozen_string_literal: true - -def create_client - Net::HTTP.start( - '127.0.0.1', - $port, - use_ssl: true, - verify_mode: OpenSSL::SSL::VERIFY_NONE, - ) { |http| yield(http) } -end - -def req_set_headers(req) - req['Content-Type'] = 'application/json' - req['Authorization'] = "Basic #{$token.chomp}" -end - -def request_get(path) - create_client do |http| - uri = URI("#{$host}/#{path}") - req = Net::HTTP::Get.new(uri) - req_set_headers(req) - res = http.request req - JSON.parse(res.body) - end -end - -def request_post(path, body) - create_client do |http| - uri = URI("#{$host}/#{path}") - req = Net::HTTP::Post.new(uri, "Content-Type": 'application/json') - req.body = body - req_set_headers(req) - res = http.request req - JSON.parse(res.body) - end -end - -def refresh_loot() - request_post('lol-loot/v1/refresh?force=true', '') -end - -def get_current_summoner() - request_get('lol-summoner/v1/current-summoner') -end - -def get_player_loot() - request_get('lol-loot/v1/player-loot') -end - -def get_champion_mastery(summoner_id) - request_get("lol-collections/v1/inventories/#{summoner_id}/champion-mastery") -end - -def get_loot_info(loot_id) - request_get("lol-loot/v1/player-loot/#{loot_id}") -end - -def get_recipes_for_item(loot_id) - request_get("lol-loot/v1/recipes/initial-item/#{loot_id}") -end - -def post_recipe(recipe, loot_ids, repeat) - $actions += repeat - - loot_id_string = "[\"" + Array(loot_ids).join("\", \"") + "\"]" - - op = - request_post( - "lol-loot/v1/recipes/#{recipe}/craft?repeat=#{repeat}", - loot_id_string - ) - - if $debug - File.open('disenchanter_post.json', 'w') { |f| f.write(op.to_json) } - puts('Okay, written to disenchanter_post.json.') - end - op -end diff --git a/src/modules/detect_client.rb b/src/modules/detect_client.rb index 12a1e7e..0c5e094 100644 --- a/src/modules/detect_client.rb +++ b/src/modules/detect_client.rb @@ -17,37 +17,41 @@ def grab_lockfile is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) if is_windows.zero? lockfile = 'lockfile' - puts 'Trying to automatically get the path of the League Client'.green begin keyname = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live' reg = Win32::Registry::HKEY_CURRENT_USER.open(keyname, Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) lockfile = "#{reg['InstallLocation']}/lockfile" + puts 'Found client via registry'.light_black rescue # do nothing end + if lockfile == 'lockfile' begin - sc = Shortcut.open('C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Riot Games\\League of Legends.lnk') + sc = Shortcut.open( + 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Riot Games\\League of Legends.lnk' + ) scpath = sc.path.split('\\') path = scpath[0..-3].join('\\') lockfile = "#{path}\\League of Legends\\lockfile" + puts 'Found client via start menu'.light_black rescue # just keep going end end end - if lockfile == 'lockfile' + begin + contents = File.read("C:\\Riot Games\\League of Legends\\#{lockfile}") + puts 'Found client at standard path'.light_black + rescue begin - contents = File.read("C:\\Riot Games\\League of Legends\\#{lockfile}") - rescue - puts 'Failed to automatically find your League Client path. ' \ - 'Please place the script directly in your League Client folder.'.light_red contents = File.read(lockfile) + rescue + puts 'Failed to automatically find your League Client path.'.light_red + puts 'Please place the script directly in your League Client folder.'.light_red end - else - contents = File.read(lockfile) end _leagueclient, _unk_port, port, password = contents.split(':') diff --git a/src/modules/stat_submission.rb b/src/modules/stat_submission.rb index fff0c9f..608e19d 100644 --- a/src/modules/stat_submission.rb +++ b/src/modules/stat_submission.rb @@ -6,7 +6,7 @@ def submit_stats(a, d, o, c, r, be, oe) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Post.new(uri, "Content-Type": 'application/json') + req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') req.body = { a: a, d: d, o: o, c: c, r: r, be: be, oe: oe }.to_json http.request(req) From 68ed46cd784c2f6b9151ce360f5cc025a7f9cda6 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 25 Jan 2024 03:37:17 +0100 Subject: [PATCH 22/63] Added hollow tacticians handler --- src/modules/handlers.rb | 1 + src/modules/handlers/tacticians.rb | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 src/modules/handlers/tacticians.rb diff --git a/src/modules/handlers.rb b/src/modules/handlers.rb index 62e7997..194b5e1 100644 --- a/src/modules/handlers.rb +++ b/src/modules/handlers.rb @@ -9,4 +9,5 @@ require_relative 'handlers/icons' require_relative 'handlers/materials' require_relative 'handlers/skins' +require_relative 'handlers/tacticians' require_relative 'handlers/wards' diff --git a/src/modules/handlers/tacticians.rb b/src/modules/handlers/tacticians.rb new file mode 100644 index 0000000..10dba5c --- /dev/null +++ b/src/modules/handlers/tacticians.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +def handle_tacticians + puts 'Not yet supported, skipping'.light_black +end From 2666ae9f07a1695778805f10cbcb3f556e1d2b73 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 25 Jan 2024 03:37:31 +0100 Subject: [PATCH 23/63] Updated stat submission structure --- src/class/statTracker.rb | 8 ++++ src/main.rb | 29 +++++------- src/modules/common_strings.rb | 16 +++++++ src/modules/stat_submission.rb | 87 +++++++++++++++------------------- 4 files changed, 74 insertions(+), 66 deletions(-) diff --git a/src/class/statTracker.rb b/src/class/statTracker.rb index 66eef69..0eb2782 100644 --- a/src/class/statTracker.rb +++ b/src/class/statTracker.rb @@ -12,6 +12,14 @@ def initialize @opened = 0 end + attr_reader :actions + attr_reader :blue_essence + attr_reader :orange_essence + attr_reader :disenchanted + attr_reader :crafted + attr_reader :redeemed + attr_reader :opened + def add_blue_essence(count) @blue_essence += count end diff --git a/src/main.rb b/src/main.rb index 3936c39..e5863e7 100644 --- a/src/main.rb +++ b/src/main.rb @@ -29,7 +29,6 @@ def run exit end - set_globals current_version = 'v1.6.0' stat_tracker = StatTracker.new client = Client.new(stat_tracker) @@ -49,7 +48,7 @@ def run end puts "\nYou're logged in as #{summoner['gameName']} ##{summoner['tagLine']}.".light_blue puts separator - puts "\nFeel free to try the options, no actions will be taken until you confirm a banner like this:".light_blue + puts "\nYour loot is safe, no actions will be taken until you confirm a banner like this:".light_blue puts 'CONFIRM: Perform this action? [y|n]'.light_magenta puts separator @@ -58,7 +57,7 @@ def run '1' => 'Materials', '2' => 'Champions', '3' => 'Skins', - #"4" => "Tacticians", + '4' => 'Tacticians', '5' => 'Eternals', '6' => 'Emotes', '7' => 'Ward Skins', @@ -83,8 +82,8 @@ def run todo = user_input_check( - "\nWhat would you like to do? (Hint: do Materials first so you don't miss anything!)\n\n".light_cyan + - todo_string + 'Option: ', + "\nWhat would you like to do? (Hint: go top to bottom so you don't miss anything!)\n\n".light_cyan + + "#{todo_string}Option: ", things_todo.keys, '', '' @@ -103,8 +102,8 @@ def run handle_champions when '3' handle_skins - # when "4" - # handle_tacticians + when '4' + handle_tacticians when '5' handle_eternals when '6' @@ -122,16 +121,17 @@ def run when 'x' done = true end - refresh_loot + client.refresh_loot puts separator end puts "That's it!".light_green - if $actions > 0 - puts "We saved you about #{$actions * 3} seconds of waiting for animations to finish.".light_green + stat_tracker.add_actions(2) + if stat_tracker.actions.positive? + puts "We saved you about #{stat_tracker.actions * 3} seconds of waiting for animations to finish.".light_green puts separator end - handle_stat_submission + handle_stat_submission(stat_tracker) puts 'See you next time :)'.light_green ask exit_string end @@ -140,11 +140,4 @@ def pad(str, len, right = true) "%#{right ? '-' : ''}#{len}s" % str end -def set_globals - $ans_yn = %w[y yes n no] - $ans_y = %w[y yes] - $ans_n = %w[n no] - $ans_yn_d = '[y|n]' -end - run diff --git a/src/modules/common_strings.rb b/src/modules/common_strings.rb index e2133dd..49a56de 100644 --- a/src/modules/common_strings.rb +++ b/src/modules/common_strings.rb @@ -7,3 +7,19 @@ def exit_string def separator '____________________________________________________________'.light_black end + +def ans_yn + %w[y yes n no] +end + +def ans_y + %w[y yes] +end + +def ans_n + %w[n no] +end + +def ans_yn_d + '[y|n]' +end diff --git a/src/modules/stat_submission.rb b/src/modules/stat_submission.rb index 608e19d..a01cd29 100644 --- a/src/modules/stat_submission.rb +++ b/src/modules/stat_submission.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -def submit_stats(a, d, o, c, r, be, oe) +def submit_stats(stat_tracker) begin uri = URI('https://checksch.de/hook/disenchanter.php') http = Net::HTTP.new(uri.host, uri.port) @@ -8,57 +8,48 @@ def submit_stats(a, d, o, c, r, be, oe) http.verify_mode = OpenSSL::SSL::VERIFY_NONE req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') - req.body = { a: a, d: d, o: o, c: c, r: r, be: be, oe: oe }.to_json + req.body = { a: stat_tracker.actions, + d: stat_tracker.disenchanted, + o: stat_tracker.opened, + c: stat_tracker.crafted, + r: stat_tracker.redeemed, + be: stat_tracker.blue_essence, + oe: stat_tracker.orange_essence }.to_json http.request(req) - rescue => exception - handle_exception(exception, 'stat submission') + rescue => e + handle_exception(e, 'stat submission') end end -def handle_stat_submission - if $actions > 0 - strlen = 15 - numlen = 7 - stats_string = "Your stats:\n".light_blue - stats_string += - pad('Actions', strlen) + pad($actions.to_s, numlen, false).light_white + - "\n" - stats_string += - pad('Disenchanted', strlen) + - pad($s_disenchanted.to_s, numlen, false).light_white + "\n" - stats_string += - pad('Opened', strlen) + pad($s_opened.to_s, numlen, false).light_white + - "\n" - stats_string += - pad('Crafted', strlen) + pad($s_crafted.to_s, numlen, false).light_white + - "\n" - stats_string += - pad('Redeemed', strlen) + - pad($s_redeemed.to_s, numlen, false).light_white + "\n" - stats_string += - pad('Blue Essence', strlen) + - pad($s_blue_essence.to_s, numlen, false).light_white + "\n" - stats_string += - pad('Orange Essence', strlen) + - pad($s_orange_essence.to_s, numlen, false).light_white + "\n" +def handle_stat_submission(stat_tracker) + return if stat_tracker.actions.zero? - if ($ans_y).include? user_input_check( - "Would you like to contribute your (anonymous) stats to the global stats?\n".light_cyan + - stats_string + '[y|n]: ', - $ans_yn, - $ans_yn_d, - '' - ) - submit_stats( - $actions, - $s_disenchanted, - $s_opened, - $s_crafted, - $s_redeemed, - $s_blue_essence, - $s_orange_essence - ) - puts 'Thank you very much!'.light_green - end + if ans_y.include? user_input_check( + "Would you like to contribute your (anonymous) stats to the global stats?\n".light_cyan + + "#{gather_stats(stat_tracker)}[y|n]: ", + ans_yn, + ans_yn_d, + '' + ) + submit_stats(stat_tracker) + puts 'Thank you very much!'.light_green end end + +def gather_stats(stat_tracker) + out = "Your stats:\n".light_blue + stats = ['Actions', 'Disenchanted', 'Opened', 'Crafted', 'Redeemed', 'Blue Essence', 'Orange Essence'] + + out += stats.map { |stat| wrap_stat_line(stat, stat_tracker.send(stat.downcase.gsub(' ', '_'))) }.join + out +end + +def wrap_stat_line(name, value) + strlen = 15 + numlen = 7 + out = pad(name, strlen) + out += pad(value.to_s, numlen, false).light_white + out += "\n" + + out +end From 246d4e2599bf7a93eab13367b921f1c56db45d0c Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sun, 28 Jan 2024 20:53:56 +0100 Subject: [PATCH 24/63] Polished code style --- src/class/statTracker.rb | 8 +------ src/modules/stat_submission.rb | 41 +++++++++++++++++----------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/class/statTracker.rb b/src/class/statTracker.rb index 0eb2782..0bbfe48 100644 --- a/src/class/statTracker.rb +++ b/src/class/statTracker.rb @@ -12,13 +12,7 @@ def initialize @opened = 0 end - attr_reader :actions - attr_reader :blue_essence - attr_reader :orange_essence - attr_reader :disenchanted - attr_reader :crafted - attr_reader :redeemed - attr_reader :opened + attr_reader :actions, :blue_essence, :orange_essence, :disenchanted, :crafted, :redeemed, :opened def add_blue_essence(count) @blue_essence += count diff --git a/src/modules/stat_submission.rb b/src/modules/stat_submission.rb index a01cd29..23da6f8 100644 --- a/src/modules/stat_submission.rb +++ b/src/modules/stat_submission.rb @@ -1,24 +1,26 @@ # frozen_string_literal: true def submit_stats(stat_tracker) - begin - uri = URI('https://checksch.de/hook/disenchanter.php') - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') - - req.body = { a: stat_tracker.actions, - d: stat_tracker.disenchanted, - o: stat_tracker.opened, - c: stat_tracker.crafted, - r: stat_tracker.redeemed, - be: stat_tracker.blue_essence, - oe: stat_tracker.orange_essence }.to_json - http.request(req) - rescue => e - handle_exception(e, 'stat submission') - end + uri = URI('https://checksch.de/hook/disenchanter.php') + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + http.request(build_stat_request(stat_tracker)) +rescue StandardError => e + handle_exception(e, 'stat submission') +end + +def build_stat_request(stat_tracker) + req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') + req.body = { a: stat_tracker.actions, + d: stat_tracker.disenchanted, + o: stat_tracker.opened, + c: stat_tracker.crafted, + r: stat_tracker.redeemed, + be: stat_tracker.blue_essence, + oe: stat_tracker.orange_essence }.to_json + + req end def handle_stat_submission(stat_tracker) @@ -27,8 +29,7 @@ def handle_stat_submission(stat_tracker) if ans_y.include? user_input_check( "Would you like to contribute your (anonymous) stats to the global stats?\n".light_cyan + "#{gather_stats(stat_tracker)}[y|n]: ", - ans_yn, - ans_yn_d, + ans_yn, ans_yn_d, '' ) submit_stats(stat_tracker) From 9c8001d2415e525ddac6e6a4d8a2d60689422c5d Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sun, 28 Jan 2024 23:56:16 +0100 Subject: [PATCH 25/63] Updated dependencies --- Gemfile.lock | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8c9624f..69731c8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,14 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.8.1) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) base64 (0.1.1) colorize (0.8.1) - date (3.2.2) - json (2.6.3) + date (3.3.4) + json (2.7.1) + language_server-protocol (3.17.0.3) launchy (2.5.2) addressable (~> 2.8) ocra (1.3.11) @@ -15,32 +16,35 @@ GEM stringio time uri - parallel (1.22.1) - parser (3.2.2.0) + parallel (1.24.0) + parser (3.3.0.5) ast (~> 2.4.1) - public_suffix (5.0.1) + racc + public_suffix (5.0.4) + racc (1.7.3) rainbow (3.1.1) - regexp_parser (2.8.0) - rexml (3.2.5) - rubocop (1.50.2) + regexp_parser (2.9.0) + rexml (3.2.6) + rubocop (1.60.2) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.0, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.28.0) + rubocop-ast (1.30.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) - rufo (0.16.0) - stringio (3.0.2) - time (0.2.0) + rufo (0.17.0) + stringio (3.1.0) + time (0.3.0) date - unicode-display_width (2.4.2) - uri (0.11.0) + unicode-display_width (2.5.0) + uri (0.13.0) win32-shortcut (0.3.0) PLATFORMS From ef2dfd9089ef19e14a2eb4d52526035089ef2dbf Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sun, 28 Jan 2024 23:56:57 +0100 Subject: [PATCH 26/63] Added RuboCop to CI action --- .github/workflows/ci.yml | 23 +++++++++++++++++++++-- .gitignore | 3 +++ sonar-project.properties | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9841389..7abdcdc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,16 +12,35 @@ on: jobs: analyze: name: Analyze + if: github.event_name != 'pull_request' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest permissions: read-all steps: - - uses: actions/checkout@v4 + - name: Check out code + uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2.0 + bundler-cache: true + + - name: Install dependencies + run: bundle install + + - name: RuboCop scan + run: bundle exec rubocop + + - name: Upload RuboCop report + uses: actions/upload-artifact@v2 + with: + name: rubocop-report + path: rubocop-report.json + - name: SonarQube scan - if: github.event_name != 'pull_request' || github.event_name == 'workflow_dispatch' uses: sonarsource/sonarqube-scan-action@master env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore index 32268dd..5f88fcd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ disenchanter_up.exe tmpin tmpout +settings.json +rubocop-report.json + *.gem *.rbc /.config diff --git a/sonar-project.properties b/sonar-project.properties index bb3bc54..feb5804 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,3 +5,5 @@ sonar.sources=. sonar.sourceEncoding=UTF-8 sonar.coverage.exclusions=**Test.php,**test.php,**.test.js,pages/** + +sonar.ruby.rubocop.reportPaths=rubocop-report.json From 1a8ac9dd481f71747f66c4f41c2f2c739eb36d3c Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sun, 28 Jan 2024 23:57:06 +0100 Subject: [PATCH 27/63] Restructuring --- src/class/client.rb | 16 +- src/class/{statTracker.rb => stat_tracker.rb} | 0 src/main.rb | 19 +- src/modules/handlers/materials.rb | 20 +- .../handlers/materials/key_fragments.rb | 54 ++--- .../handlers/materials/mythic_essence.rb | 206 +++++++++--------- src/modules/loot_metainfo.rb | 10 +- 7 files changed, 166 insertions(+), 159 deletions(-) rename src/class/{statTracker.rb => stat_tracker.rb} (100%) diff --git a/src/class/client.rb b/src/class/client.rb index f26f055..e637eb5 100644 --- a/src/class/client.rb +++ b/src/class/client.rb @@ -50,14 +50,14 @@ def request_get(path) end def request_post(path, body) - create_client do |http| - uri = URI("#{host}/#{path}") - req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') - req.body = body - req_set_headers(req) - res = http.request req - JSON.parse(res.body) - end + # create_client do |http| + # uri = URI("#{host}/#{path}") + # req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') + # req.body = body + # req_set_headers(req) + # res = http.request req + # JSON.parse(res.body) + # end end def refresh_loot diff --git a/src/class/statTracker.rb b/src/class/stat_tracker.rb similarity index 100% rename from src/class/statTracker.rb rename to src/class/stat_tracker.rb diff --git a/src/main.rb b/src/main.rb index e5863e7..ce547d1 100644 --- a/src/main.rb +++ b/src/main.rb @@ -10,7 +10,7 @@ require 'rbconfig' require_relative 'class/client' -require_relative 'class/statTracker' +require_relative 'class/stat_tracker' require_relative 'modules/common_strings' require_relative 'modules/debug' @@ -97,21 +97,21 @@ def run case todo when '1' - handle_materials + handle_materials(client, stat_tracker) when '2' - handle_champions + handle_champions(client) when '3' - handle_skins + handle_skins(client) when '4' - handle_tacticians + handle_tacticians(client) when '5' - handle_eternals + handle_eternals(client) when '6' - handle_emotes + handle_emotes(client) when '7' - handle_ward_skins + handle_ward_skins(client) when '8' - handle_icons + handle_icons(client) when 's' open_stats when 'r' @@ -126,7 +126,6 @@ def run end puts "That's it!".light_green - stat_tracker.add_actions(2) if stat_tracker.actions.positive? puts "We saved you about #{stat_tracker.actions * 3} seconds of waiting for animations to finish.".light_green puts separator diff --git a/src/modules/handlers/materials.rb b/src/modules/handlers/materials.rb index 2641b90..74463db 100644 --- a/src/modules/handlers/materials.rb +++ b/src/modules/handlers/materials.rb @@ -5,14 +5,14 @@ require_relative 'materials/mastery_tokens' require_relative 'materials/mythic_essence' -def handle_materials +def handle_materials(client, stat_tracker) done = false things_todo = { '1' => 'Mythic Essence', '2' => 'Key Fragments', '3' => 'Capsules', '4' => 'Mastery Tokens', - 'x' => 'Back to main menu', + 'x' => 'Back to main menu' } things_done = [] @@ -29,31 +29,31 @@ def handle_materials todo = user_input_check( - "\nWhat would you like to do?\n\n".light_cyan + todo_string + - 'Option: ', + "\nWhat would you like to do?\n\n".light_cyan + + "#{todo_string} Option: ", things_todo.keys, '', '' ) things_done << todo - puts $sep + puts separator puts puts "Option chosen: #{things_todo[todo]}".light_white case todo when '1' - handle_mythic_essence + handle_mythic_essence(client, stat_tracker) when '2' - handle_key_fragments + handle_key_fragments(client, stat_tracker) when '3' - handle_capsules + handle_capsules(client, stat_tracker) when '4' - handle_mastery_tokens + handle_mastery_tokens(client, stat_tracker) when 'x' done = true end - puts $sep + puts separator end end diff --git a/src/modules/handlers/materials/key_fragments.rb b/src/modules/handlers/materials/key_fragments.rb index ee95fa1..7a83396 100644 --- a/src/modules/handlers/materials/key_fragments.rb +++ b/src/modules/handlers/materials/key_fragments.rb @@ -1,31 +1,33 @@ # frozen_string_literal: true -def handle_key_fragments - begin - player_loot = get_player_loot +def handle_key_fragments(client, stat_tracker) + player_loot = client.req_get_player_loot - loot_keys = - player_loot.select { |l| l['lootId'] == 'MATERIAL_key_fragment' } - if count_loot_items(loot_keys) >= 3 - puts "Found #{count_loot_items(loot_keys)} key fragments.".light_blue - if ($ans_y).include? user_input_check( - "Craft #{(count_loot_items(loot_keys) / 3).floor} keys from #{count_loot_items(loot_keys)} key fragments?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - $s_crafted += (count_loot_items(loot_keys) / 3).floor - post_recipe( - 'MATERIAL_key_fragment_forge', - 'MATERIAL_key_fragment', - (count_loot_items(loot_keys) / 3).floor - ) - puts 'Done!'.green - end - else - puts 'Found less than 3 key fragments.'.yellow - end - rescue => exception - handle_exception(exception, 'Key Fragments') + loot_keys = + player_loot.select { |l| l['lootId'] == 'MATERIAL_key_fragment' } + fragment_count = count_loot_items(loot_keys) + key_count = (count_loot_items(loot_keys) / 3).floor + + if fragment_count < 3 + puts 'Not enough key fragments to craft anything.'.yellow + return + end + + puts "Found #{fragment_count} key fragments.".light_blue + if ans_y.include? user_input_check( + "Craft #{key_count} keys from #{fragment_count} key fragments?", + ans_yn, + ans_yn_d, + 'confirm' + ) + stat_tracker.add_crafted(key_count) + client.req_post_recipe( + 'MATERIAL_key_fragment_forge', + 'MATERIAL_key_fragment', + key_count + ) + puts 'Done!'.green end +rescue => e + handle_exception(e, 'Key Fragments') end diff --git a/src/modules/handlers/materials/mythic_essence.rb b/src/modules/handlers/materials/mythic_essence.rb index 6a74a63..8f73115 100644 --- a/src/modules/handlers/materials/mythic_essence.rb +++ b/src/modules/handlers/materials/mythic_essence.rb @@ -1,105 +1,111 @@ # frozen_string_literal: true -def handle_mythic_essence - begin - player_loot = get_player_loot - mythic_loot_id = 'CURRENCY_mythic' - - loot_essence = player_loot.select { |l| l['lootId'] == mythic_loot_id } - loot_essence = loot_essence[0] - if !loot_essence.nil? && loot_essence['count'] > 0 - puts "Found #{loot_essence['count']} Mythic Essence.".light_blue - craft_mythic_type_names = [ - 'Blue Essence', - 'Orange Essence', - 'Random Skin Shards', - ] - - craft_mythic_type = - user_input_check( - "Okay, what would you like to craft?\n" + - "[1] #{craft_mythic_type_names[0]}\n" + - "[2] #{craft_mythic_type_names[1]}\n" + - "[3] #{craft_mythic_type_names[2]}\n" + "[x] Cancel\n", - %w[1 2 3 x], - '[1|2|3|x]' - ) - - unless craft_mythic_type == 'x' - case craft_mythic_type - # Blue Essence, Orange Essence, Random Skin Shard - when '1' - recipe_target = 'CURRENCY_champion' - when '2' - recipe_target = 'CURRENCY_cosmetic' - when '3' - recipe_target = 'CHEST_291' - end - - recipes = get_recipes_for_item(mythic_loot_id) - recipes = - recipes.select { |r| r['outputs'][0]['lootName'] == recipe_target } - unless recipes.length == 0 - recipe = recipes[0] - - puts "Recipe found: #{recipe['contextMenuText']} for #{recipe['slots'][0]['quantity']} Mythic Essence".light_blue - - craft_mythic_amount = - user_input_check( - "Alright, how much Mythic Essence should we use to craft #{craft_mythic_type_names[craft_mythic_type.to_i - 1]}?", - (1..loot_essence['count'].to_i) - .to_a - .append('all') - .map! { |n| n.to_s }, - "[1..#{loot_essence['count']}|all]" - ) - - if craft_mythic_amount == 'all' - craft_mythic_amount = loot_essence['count'] - end - craft_mythic_amount = craft_mythic_amount.to_i - - could_craft = - (craft_mythic_amount / recipe['slots'][0]['quantity']).floor - unless could_craft < 1 - if ($ans_y).include? user_input_check( - "Craft #{could_craft * recipe['outputs'][0]['quantity']} " + - "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]} from " + - "#{(craft_mythic_amount / recipe['slots'][0]['quantity']).floor * recipe['slots'][0]['quantity']} Mythic Essence?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - case craft_mythic_type - when '1' - $s_blue_essence += - could_craft * recipe['outputs'][0]['quantity'] - when '2' - $s_orange_essence += - could_craft * recipe['outputs'][0]['quantity'] - end - $s_crafted += could_craft - - post_recipe( - recipe['recipeName'], - mythic_loot_id, - (craft_mythic_amount / recipe['slots'][0]['quantity']).floor - ) - puts 'Done!'.green - end - else - puts 'Not enough Mythic Essence for that recipe.'.yellow - end - else - puts "Recipes for #{craft_mythic_type_names[craft_mythic_type.to_i - 1]} seem to be unavailable.".yellow - end - else - puts 'Mythic crafting canceled.'.yellow - end - else - puts 'Found no Mythic Essence to use.'.yellow +def handle_mythic_essence(client, stat_tracker) + player_loot = client.req_get_player_loot + mythic_loot_id = 'CURRENCY_mythic' + + loot_essence = player_loot.select { |l| l['lootId'] == mythic_loot_id } + loot_essence = loot_essence[0] + + if loot_essence.nil? || loot_essence['count'].zero? + puts 'Found no Mythic Essence to use.'.yellow + return + end + + puts "Found #{loot_essence['count']} Mythic Essence.".light_blue + craft_mythic_type_names = [ + 'Blue Essence', + 'Orange Essence', + 'Random Skin Shards' + ] + + craft_mythic_type = + user_input_check( + "Okay, what would you like to craft?\n" + + "[1] #{craft_mythic_type_names[0]}\n" + + "[2] #{craft_mythic_type_names[1]}\n" + + "[3] #{craft_mythic_type_names[2]}\n" + "[x] Cancel\n", + %w[1 2 3 x], + '[1|2|3|x]' + ) + + case craft_mythic_type + # Blue Essence, Orange Essence, Random Skin Shard + when '1' + recipe_target = 'CURRENCY_champion' + when '2' + recipe_target = 'CURRENCY_cosmetic' + when '3' + recipe_target = 'CHEST_291' + when 'x' + puts 'Mythic crafting canceled.'.yellow + return + end + + recipes = client.req_get_recipes_for_item(mythic_loot_id) + recipes = + recipes.select { |r| r['outputs'][0]['lootName'] == recipe_target } + + if recipes.empty? + puts "Recipes for #{craft_mythic_type_names[craft_mythic_type.to_i - 1]} seem to be unavailable.".yellow + return + end + recipe = recipes[0] + + puts "Recipe found: #{recipe['contextMenuText']} for " \ + "#{recipe['slots'][0]['quantity']} Mythic Essence".light_blue + + craft_mythic_amount = + user_input_check( + 'Alright, how much Mythic Essence should we use to craft ' \ + "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]}?", + (1..loot_essence['count'].to_i) + .to_a + .append('all') + .map!(&:to_s), + "[1..#{loot_essence['count']}|all|x]" + ) + + if craft_mythic_amount == 'x' + puts 'Mythic crafting canceled.'.yellow + return + end + craft_mythic_amount = loot_essence['count'] if craft_mythic_amount == 'all' + craft_mythic_amount = craft_mythic_amount.to_i + + could_craft = + (craft_mythic_amount / recipe['slots'][0]['quantity']).floor + + if could_craft.zero? + puts 'Not enough Mythic Essence for that recipe.'.yellow + return + end + + craft_quantity = could_craft * recipe['outputs'][0]['quantity'] + craft_target_name = craft_mythic_type_names[craft_mythic_type.to_i - 1] + craft_price = could_craft * recipe['slots'][0]['quantity'] + + if ans_y.include? user_input_check( + "Craft #{craft_quantity} #{craft_target_name} from #{craft_price} Mythic Essence?", + ans_yn, + ans_yn_d, + 'confirm' + ) + case craft_mythic_type + when '1' + stat_tracker.add_blue_essence(craft_quantity) + when '2' + stat_tracker.add_orange_essence(craft_quantity) end - rescue => exception - handle_exception(exception, 'Mythic Essence') + stat_tracker.add_crafted(could_craft) + + post_recipe( + recipe['recipeName'], + mythic_loot_id, + (craft_mythic_amount / recipe['slots'][0]['quantity']).floor + ) + puts 'Done!'.green end +rescue => e + handle_exception(e, 'Mythic Essence') end diff --git a/src/modules/loot_metainfo.rb b/src/modules/loot_metainfo.rb index 61e4fe5..7003a6d 100644 --- a/src/modules/loot_metainfo.rb +++ b/src/modules/loot_metainfo.rb @@ -2,21 +2,21 @@ def count_loot_items(loot_items) count = 0 - unless loot_items.nil? || loot_items.empty? - loot_items.each { |loot| count += loot['count'] } - end + return count if loot_items.nil? || loot_items.empty? + + loot_items.each { |loot| count += loot['count'] } count end def get_chest_name(loot_id) chest_info = get_loot_info(loot_id) - return chest_info['localizedName'] if !chest_info['localizedName'].empty? + return chest_info['localizedName'] unless chest_info['localizedName'].empty? catalogue = { 'CHEST_128' => 'Champion Capsule', 'CHEST_129' => 'Glorious Champion Capsule', 'CHEST_210' => 'Honor Level 4 Orb', - 'CHEST_211' => 'Honor Level 5 Orb', + 'CHEST_211' => 'Honor Level 5 Orb' } return catalogue[loot_id] if catalogue.key?(loot_id) From 3dcd2c004a806067a17affd906c215b99dabd8c9 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Mon, 29 Jan 2024 00:00:48 +0100 Subject: [PATCH 28/63] Aligned Ruby version with Gemfile --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7abdcdc..c46b39d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 3.2.0 + ruby-version: 3.1.2 bundler-cache: true - name: Install dependencies From 719d9e6f1ad4f0c982ac4feb183342f94829e243 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Mon, 29 Jan 2024 02:17:41 +0100 Subject: [PATCH 29/63] Updated dependencies + rubocop settings --- .rubocop.yml | 7 +++++++ Gemfile | 2 +- Gemfile.lock | 11 ++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index fa25733..63be77f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,3 +2,10 @@ AllCops: NewCops: enable Layout/EndOfLine: EnforcedStyle: lf +Metrics/MethodLength: + CountAsOne: ['array', 'method_call', 'hash'] + Max: 30 + Severity: refactor +Metrics/CyclomaticComplexity: + Max: 12 + Severity: refactor diff --git a/Gemfile b/Gemfile index f3f9583..5e78b2d 100644 --- a/Gemfile +++ b/Gemfile @@ -15,5 +15,5 @@ group :development do # Ruby formatter, config in .rufo gem 'rufo', '>= 0.13.0', require: false # Ruby linter, config in .rubocop - gem 'rubocop', '~> 1.50', require: false + gem 'rubocop', '~> 1.60', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 69731c8..08f53c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,6 +20,7 @@ GEM parser (3.3.0.5) ast (~> 2.4.1) racc + prettier_print (1.2.1) public_suffix (5.0.4) racc (1.7.3) rainbow (3.1.1) @@ -38,9 +39,16 @@ GEM unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.30.0) parser (>= 3.2.1.0) + ruby-lsp (0.5.1) + language_server-protocol (~> 3.17.0) + sorbet-runtime + syntax_tree (>= 6.1.1, < 7) ruby-progressbar (1.13.0) rufo (0.17.0) + sorbet-runtime (0.5.11219) stringio (3.1.0) + syntax_tree (6.2.0) + prettier_print (>= 1.2.0) time (0.3.0) date unicode-display_width (2.5.0) @@ -59,7 +67,8 @@ DEPENDENCIES launchy (~> 2.5) ocra (= 1.3.11) open-uri (~> 0.2.0) - rubocop (~> 1.50) + rubocop (~> 1.60) + ruby-lsp (~> 0.5.1) rufo (>= 0.13.0) win32-shortcut (~> 0.3.0) From c4c2fe96a73558bad86c83ad2402b2cf3cf4e8ed Mon Sep 17 00:00:00 2001 From: marvinscham Date: Mon, 29 Jan 2024 02:17:57 +0100 Subject: [PATCH 30/63] Added report output to rubocop cmd --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c46b39d..65cfc17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: run: bundle install - name: RuboCop scan - run: bundle exec rubocop + run: bundle exec rubocop --format json -o rubocop-report.json || echo "Failed on RuboCop rules" - name: Upload RuboCop report uses: actions/upload-artifact@v2 From 20887787076ff0342911721215930ef728c58503 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Mon, 29 Jan 2024 02:18:16 +0100 Subject: [PATCH 31/63] Refactoring + touchups --- src/main.rb | 4 +- .../handlers/materials/mastery_tokens.rb | 221 +++++++++--------- 2 files changed, 114 insertions(+), 111 deletions(-) diff --git a/src/main.rb b/src/main.rb index ce547d1..7fd16c5 100644 --- a/src/main.rb +++ b/src/main.rb @@ -135,8 +135,8 @@ def run ask exit_string end -def pad(str, len, right = true) - "%#{right ? '-' : ''}#{len}s" % str +def pad(str, len, right: true) + format("%#{right ? '-' : ''}#{len}s", str) end run diff --git a/src/modules/handlers/materials/mastery_tokens.rb b/src/modules/handlers/materials/mastery_tokens.rb index afc633c..f339e2f 100644 --- a/src/modules/handlers/materials/mastery_tokens.rb +++ b/src/modules/handlers/materials/mastery_tokens.rb @@ -1,125 +1,128 @@ # frozen_string_literal: true -def handle_mastery_tokens - begin - player_loot = get_player_loot - loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } - loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } +def handle_mastery_tokens(client, stat_tracker) + player_loot = client.req_get_player_loot + loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } + loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } - recipes6 = get_recipes_for_item('CHAMPION_TOKEN_6-1') - recipes7 = get_recipes_for_item('CHAMPION_TOKEN_7-1') - recipe6_cost = - recipes6.select do |r| - r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' - end - recipe6_cost = recipe6_cost[0]['slots'][1]['quantity'] - recipe7_cost = - recipes7.select do |r| - r['recipeName'] == 'CHAMPION_TOKEN_7_redeem_withessence' - end - recipe7_cost = recipe7_cost[0]['slots'][1]['quantity'] + recipes6 = get_recipes_for_item('CHAMPION_TOKEN_6-1') + recipes7 = get_recipes_for_item('CHAMPION_TOKEN_7-1') + recipe6_cost = + recipes6.select do |r| + r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' + end + recipe6_cost = recipe6_cost[0]['slots'][1]['quantity'] + recipe7_cost = + recipes7.select do |r| + r['recipeName'] == 'CHAMPION_TOKEN_7_redeem_withessence' + end + recipe7_cost = recipe7_cost[0]['slots'][1]['quantity'] - loot_overall_tokens = - player_loot.select do |l| - (l['lootName'] == 'CHAMPION_TOKEN_6') || - (l['lootName'] == 'CHAMPION_TOKEN_7') - end + loot_overall_tokens = + player_loot.select do |l| + (l['lootName'] == 'CHAMPION_TOKEN_6') || + (l['lootName'] == 'CHAMPION_TOKEN_7') + end - puts "Found #{count_loot_items(loot_overall_tokens)} Mastery Tokens.".light_blue + puts "Found #{count_loot_items(loot_overall_tokens)} Mastery Tokens.".light_blue - loot_mastery_tokens = - player_loot.select do |l| - (l['lootName'] == 'CHAMPION_TOKEN_6' && l['count'] == 2) || - (l['lootName'] == 'CHAMPION_TOKEN_7' && l['count'] == 3) - end + loot_mastery_tokens = + player_loot.select do |l| + (l['lootName'] == 'CHAMPION_TOKEN_6' && l['count'] == 2) || + (l['lootName'] == 'CHAMPION_TOKEN_7' && l['count'] == 3) + end - if loot_mastery_tokens.count > 0 - loot_mastery_tokens = - loot_mastery_tokens.sort_by { |l| [l['lootName'], l['itemDesc']] } - puts "We could upgrade the following champions:\n".light_blue - needed_shards = 0 - needed_perms = 0 - needed_essence = 0 + if loot_mastery_tokens.count.zero? + puts 'Found no upgradable set of Mastery Tokens.'.yellow + return + end - loot_mastery_tokens.each do |t| - ref_shard = - loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } - ref_perm = - loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } + loot_mastery_tokens = + loot_mastery_tokens.sort_by { |l| [l['lootName'], l['itemDesc']] } + puts "We could upgrade the following champions:\n".light_blue + needed_shards = 0 + needed_perms = 0 + needed_essence = 0 - print pad(t['itemDesc'], 15, false).light_white - print ' to Mastery Level '.light_black - print "#{(t['lootName'])[-1]}".light_white - print ' using '.light_black - if !ref_shard.empty? && ref_shard[0]['count'] > 0 - print 'a champion shard.'.green - needed_shards += 1 - t['upgrade_type'] = 'shard' - elsif !ref_perm.empty? && ref_shard[0]['count'] > 0 - print 'a champion permanent.'.green - needed_perms += 1 - t['upgrade_type'] = 'permanent' - else - recipe_cost = (t['lootName'])[-1] == '6' ? recipe6_cost : recipe7_cost - print "#{recipe_cost} Blue Essence.".yellow - needed_essence += recipe_cost - t['upgrade_type'] = 'essence' - end - puts - end - puts + loot_mastery_tokens.each do |t| + ref_shard = + loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } + ref_perm = + loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } - owned_essence = - player_loot.select { |l| l['lootId'] == 'CURRENCY_champion' } - owned_essence = owned_essence[0]['count'] - if (owned_essence > needed_essence) - question_string = - "Upgrade #{loot_mastery_tokens.count} champions using " - question_string += "#{needed_shards} Shards, " if needed_shards > 0 - question_string += "#{needed_perms} Permanents, " if needed_perms > 0 - question_string += - "#{needed_essence} Blue Essence, " if needed_essence > 0 - question_string = question_string.delete_suffix(', ') - question_string += '?' + print pad(t['itemDesc'], 15, false).light_white + print ' to Mastery Level '.light_black + print (t['lootName'])[-1].light_white + print ' using '.light_black + if !ref_shard.empty? && ref_shard[0]['count'].positive? + print 'a champion shard.'.green + needed_shards += 1 + t['upgrade_type'] = 'shard' + elsif !ref_perm.empty? && ref_shard[0]['count'].positive? + print 'a champion permanent.'.green + needed_perms += 1 + t['upgrade_type'] = 'permanent' + else + recipe_cost = (t['lootName'])[-1] == '6' ? recipe6_cost : recipe7_cost + print "#{recipe_cost} Blue Essence.".yellow + needed_essence += recipe_cost + t['upgrade_type'] = 'essence' + end + puts + end + puts + + owned_essence = + player_loot.select { |l| l['lootId'] == 'CURRENCY_champion' } + owned_essence = owned_essence[0]['count'] + + if owned_essence < needed_essence + puts "You're missing #{needed_essence - owned_essence} Blue Essence needed to proceed. Skipping...".yellow + return + end - if $ans_y.include? user_input_check( - question_string, - $ans_yn, - $ans_yn_d, - 'confirm' + question_string = + "Upgrade #{loot_mastery_tokens.count} champions using " + question_string += "#{needed_shards} Shards, " if needed_shards.positive? + question_string += "#{needed_perms} Permanents, " if needed_perms.positive? + if needed_essence.positive? + question_string += + "#{needed_essence} Blue Essence, " + end + question_string = question_string.delete_suffix(', ') + question_string += '?' + + if ans_y.include? user_input_check( + question_string, + ans_yn, + ans_yn_d, + 'confirm' + ) + loot_mastery_tokens.each do |t| + stat_tracker.add_redeemed(1) + target_level = (t['lootName'])[-1] + case t['upgrade_type'] + when 'shard' + post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withshard", + [t['lootId'], "CHAMPION_RENTAL_#{t['refId']}"], + 1 + ) + when 'permanent' + post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withpermanent", + [t['lootId'], "CHAMPION_#{t['refId']}"], + 1 + ) + when 'essence' + post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withessence", + [t['lootId'], 'CURRENCY_champion'], + 1 ) - loot_mastery_tokens.each do |t| - $s_redeemed += 1 - target_level = (t['lootName'])[-1] - case t['upgrade_type'] - when 'shard' - post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withshard", - [t['lootId'], "CHAMPION_RENTAL_#{t['refId']}"], - 1 - ) - when 'permanent' - post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withpermanent", - [t['lootId'], "CHAMPION_#{t['refId']}"], - 1 - ) - when 'essence' - post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withessence", - [t['lootId'], 'CURRENCY_champion'], - 1 - ) - end - end - end - else - puts "You're missing #{needed_essence - owned_essence} Blue Essence needed to proceed. Skipping...".yellow end - else - puts 'Found no upgradable set of Mastery Tokens.'.yellow end - rescue => exception - handle_exception(exception, 'token upgrades') end +rescue StandardError => e + handle_exception(e, 'token upgrades') end From 007549fc012c317b1ae285d7c69dc47fd28ac1cc Mon Sep 17 00:00:00 2001 From: marvinscham Date: Mon, 29 Jan 2024 02:20:45 +0100 Subject: [PATCH 32/63] Fixed client post calls --- src/modules/handlers/materials/mastery_tokens.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/modules/handlers/materials/mastery_tokens.rb b/src/modules/handlers/materials/mastery_tokens.rb index f339e2f..a0e6fba 100644 --- a/src/modules/handlers/materials/mastery_tokens.rb +++ b/src/modules/handlers/materials/mastery_tokens.rb @@ -48,7 +48,7 @@ def handle_mastery_tokens(client, stat_tracker) ref_shard = loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } ref_perm = - loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } + loot_perms.select { |l| t['refId'] == l['storeItemId'].to_s } print pad(t['itemDesc'], 15, false).light_white print ' to Mastery Level '.light_black @@ -99,28 +99,29 @@ def handle_mastery_tokens(client, stat_tracker) 'confirm' ) loot_mastery_tokens.each do |t| - stat_tracker.add_redeemed(1) target_level = (t['lootName'])[-1] case t['upgrade_type'] when 'shard' - post_recipe( + client.req_post_recipe( "CHAMPION_TOKEN_#{target_level}_redeem_withshard", [t['lootId'], "CHAMPION_RENTAL_#{t['refId']}"], 1 ) when 'permanent' - post_recipe( + client.req_post_recipe( "CHAMPION_TOKEN_#{target_level}_redeem_withpermanent", [t['lootId'], "CHAMPION_#{t['refId']}"], 1 ) when 'essence' - post_recipe( + client.req_post_recipe( "CHAMPION_TOKEN_#{target_level}_redeem_withessence", [t['lootId'], 'CURRENCY_champion'], 1 ) end + + stat_tracker.add_redeemed(1) end end rescue StandardError => e From f1b64d6f344b952cbed86cd6b753de06e79a7bcf Mon Sep 17 00:00:00 2001 From: marvinscham Date: Mon, 29 Jan 2024 02:52:51 +0100 Subject: [PATCH 33/63] Expanded complexity limit settings --- .rubocop.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 63be77f..55e0c33 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,3 +9,9 @@ Metrics/MethodLength: Metrics/CyclomaticComplexity: Max: 12 Severity: refactor +Metrics/PerceivedComplexity: + Max: 12 + Severity: refactor +Metrics/AbcSize: + Max: 25 + Severity: refactor From a98ab9742c0ddbbca12e865c272b76064307bd3b Mon Sep 17 00:00:00 2001 From: marvinscham Date: Mon, 29 Jan 2024 02:53:06 +0100 Subject: [PATCH 34/63] Split methods, reworked --- src/modules/handlers/materials/capsules.rb | 100 +++++++++++---------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/src/modules/handlers/materials/capsules.rb b/src/modules/handlers/materials/capsules.rb index 6c41d8d..2ba0e3e 100644 --- a/src/modules/handlers/materials/capsules.rb +++ b/src/modules/handlers/materials/capsules.rb @@ -1,53 +1,63 @@ # frozen_string_literal: true -def handle_capsules - begin - player_loot = get_player_loot - - loot_capsules = - player_loot.select { |l| l['lootName'].start_with?('CHEST_') } - loot_capsules.each do |c| - recipes = get_recipes_for_item(c['lootId']) - if recipes[0]['slots'].length > 1 || !recipes[0]['type'] == 'OPEN' - c['needs_key'] = true - else - c['needs_key'] = false - end - end - loot_capsules = loot_capsules.select { |c| c['needs_key'] == false } +def handle_capsules(client, stat_tracker) + player_loot = client.req_get_player_loot - if count_loot_items(loot_capsules) > 0 - puts "Found #{count_loot_items(loot_capsules)} capsules:".light_blue - loot_capsules.each do |c| - puts "#{c['count']}x ".light_black + - "#{get_chest_name(c['lootId'])}".light_white - end + loot_capsules = filter_key_capsules(player_loot) + + if count_loot_items(loot_capsules).zero? + puts 'Found no keyless capsules to open.'.yellow + return + end + + print_capsule_summary(loot_capsules) + + if ans_y.include? user_input_check( + "Open #{count_loot_items(loot_capsules)} (keyless) capsules?", + ans_yn, ans_yn_d, 'confirm' + ) + process_keyless_capsule_requests(client, stat_tracker) + puts 'Done!'.green + end +rescue StandardError => e + handle_exception(e, 'Capsules') +end - if ($ans_y).include? user_input_check( - "Open #{count_loot_items(loot_capsules)} (keyless) capsules?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - $s_opened += count_loot_items(loot_capsules) - threads = - loot_capsules.map do |c| - Thread.new do - res = post_recipe(c['lootId'] + '_OPEN', c['lootId'], c['count']) - res['added'].each do |r| - if r['playerLoot']['lootId'] == 'CURRENCY_champion' - $s_blue_essence += r['deltaCount'] - end - end - end - end - threads.each(&:join) - puts 'Done!'.green +def filter_key_capsules(player_loot) + loot_capsules = player_loot.select { |l| l['lootName'].start_with?('CHEST_') } + + loot_capsules.each do |c| + recipes = get_recipes_for_item(c['lootId']) + c['needs_key'] = recipes[0]['slots'].length > 1 || !recipes[0]['type'] == 'OPEN' + end + + loot_capsules.select { |c| c['needs_key'] == false } +rescue StandardError => e + handle_exception(e, 'Capsules: filtering loot') +end + +def process_keyless_capsule_requests(client, stat_tracker) + threads = + loot_capsules.map do |c| + Thread.new do + res = client.req_post_recipe("#{c['lootId']}_OPEN", c['lootId'], c['count']) + res['added'].each do |r| + stat_tracker.add_blue_essence(r['deltaCount']) if r['playerLoot']['lootId'] == 'CURRENCY_champion' + end end - else - puts 'Found no keyless capsules to open.'.yellow end - rescue => exception - handle_exception(exception, 'Capsules') + threads.each(&:join) + + stat_tracker.add_opened(count_loot_items(loot_capsules)) +rescue StandardError => e + handle_exception(e, 'Capsules: request execution') +end + +def print_capsule_summary(loot_capsules) + puts "Found #{count_loot_items(loot_capsules)} capsules:".light_blue + loot_capsules.each do |c| + puts "#{c['count']}x ".light_black + get_chest_name(c['lootId']).light_white end +rescue StandardError => e + handle_exception(e, 'Capsules: summary generation') end From 52d1f4cbc8ecf6524cde95653b4f97868b558876 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Mon, 29 Jan 2024 04:00:10 +0100 Subject: [PATCH 35/63] Applied RuboCop auto fixes --- Gemfile | 2 +- src/class/client.rb | 6 +- src/main.rb | 10 +- src/modules/debug.rb | 26 +- src/modules/detect_client.rb | 8 +- src/modules/handlers/champions.rb | 234 +++++++++--------- src/modules/handlers/champions/collection.rb | 2 +- src/modules/handlers/champions/exceptions.rb | 62 +++-- src/modules/handlers/champions/mastery.rb | 83 +++---- src/modules/handlers/champions/owned.rb | 16 +- src/modules/handlers/champions/tokens.rb | 46 ++-- src/modules/handlers/generic.rb | 150 ++++++----- src/modules/handlers/materials.rb | 10 +- .../handlers/materials/key_fragments.rb | 2 +- .../handlers/materials/mythic_essence.rb | 2 +- src/modules/loot_metainfo.rb | 2 +- src/modules/update/checker.rb | 74 +++--- src/modules/user_input.rb | 8 +- 18 files changed, 356 insertions(+), 387 deletions(-) diff --git a/Gemfile b/Gemfile index 5e78b2d..50c8181 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ source 'https://rubygems.org' -git_source(:github) { |repo| 'https://github.com/#{repo}.git' } +git_source(:github) { |_repo| 'https://github.com/#{repo}.git' } ruby '3.1.2' gem 'base64', '~> 0.1.1' diff --git a/src/class/client.rb b/src/class/client.rb index e637eb5..5ecf9a1 100644 --- a/src/class/client.rb +++ b/src/class/client.rb @@ -25,13 +25,13 @@ def auth "Basic #{@token.chomp}" end - def create_client + def create_client(&) Net::HTTP.start( '127.0.0.1', @port, use_ssl: true, - verify_mode: OpenSSL::SSL::VERIFY_NONE - ) { |http| yield(http) } + verify_mode: OpenSSL::SSL::VERIFY_NONE, & + ) end def req_set_headers(req) diff --git a/src/main.rb b/src/main.rb index 7fd16c5..cd254d9 100644 --- a/src/main.rb +++ b/src/main.rb @@ -73,11 +73,11 @@ def run todo_string = '' things_todo.each do |k, v| todo_string += "[#{k}] ".light_white - if things_done.include? k - todo_string += "#{v} (done)\n".light_green - else - todo_string += "#{v}\n".light_cyan - end + todo_string += if things_done.include? k + "#{v} (done)\n".light_green + else + "#{v}\n".light_cyan + end end todo = diff --git a/src/modules/debug.rb b/src/modules/debug.rb index 7cc5bf1..59241c3 100644 --- a/src/modules/debug.rb +++ b/src/modules/debug.rb @@ -7,7 +7,7 @@ def handle_debug '2' => 'Write recipes of lootId to file', '3' => 'Write loot info of lootId to file', 'm' => 'Enable debug mode', - 'x' => 'Back to main menu', + 'x' => 'Back to main menu' } things_done = [] @@ -15,11 +15,11 @@ def handle_debug todo_string = '' things_todo.each do |k, v| todo_string += "[#{k}] ".light_white - unless things_done.include? k - todo_string += "#{v}\n".light_cyan - else - todo_string += "#{v} (done)\n".light_green - end + todo_string += if things_done.include? k + "#{v} (done)\n".light_green + else + "#{v}\n".light_cyan + end end todo = @@ -41,9 +41,7 @@ def handle_debug when '1' player_loot = get_player_loot - File.open('disenchanter_loot.json', 'w') do |f| - f.write(player_loot.to_json) - end + File.write('disenchanter_loot.json', player_loot.to_json) puts('Okay, written to disenchanter_loot.json.') when '2' @@ -51,9 +49,7 @@ def handle_debug recipes = get_recipes_for_item loot_id - File.open('disenchanter_recipes.json', 'w') do |f| - f.write(recipes.to_json) - end + File.write('disenchanter_recipes.json', recipes.to_json) puts('Okay, written to disenchanter_recipes.json.') when '3' @@ -61,9 +57,7 @@ def handle_debug loot_info = get_loot_info loot_id - File.open('disenchanter_lootinfo.json', 'w') do |f| - f.write(loot_info.to_json) - end + File.write('disenchanter_lootinfo.json', loot_info.to_json) puts('Okay, written to disenchanter_lootinfo.json.') when 'm' @@ -72,6 +66,6 @@ def handle_debug when 'x' done = true end - puts $sep + puts separator end end diff --git a/src/modules/detect_client.rb b/src/modules/detect_client.rb index 0c5e094..2c4bf27 100644 --- a/src/modules/detect_client.rb +++ b/src/modules/detect_client.rb @@ -23,7 +23,7 @@ def grab_lockfile Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) lockfile = "#{reg['InstallLocation']}/lockfile" puts 'Found client via registry'.light_black - rescue + rescue StandardError # do nothing end @@ -36,7 +36,7 @@ def grab_lockfile path = scpath[0..-3].join('\\') lockfile = "#{path}\\League of Legends\\lockfile" puts 'Found client via start menu'.light_black - rescue + rescue StandardError # just keep going end end @@ -45,10 +45,10 @@ def grab_lockfile begin contents = File.read("C:\\Riot Games\\League of Legends\\#{lockfile}") puts 'Found client at standard path'.light_black - rescue + rescue StandardError begin contents = File.read(lockfile) - rescue + rescue StandardError puts 'Failed to automatically find your League Client path.'.light_red puts 'Please place the script directly in your League Client folder.'.light_red end diff --git a/src/modules/handlers/champions.rb b/src/modules/handlers/champions.rb index a5880d0..7acb583 100644 --- a/src/modules/handlers/champions.rb +++ b/src/modules/handlers/champions.rb @@ -7,146 +7,142 @@ require_relative 'champions/tokens' def handle_champions - begin - player_loot = get_player_loot - loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } + player_loot = get_player_loot + loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } - loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } - if count_loot_items(loot_perms) > 0 - if ($ans_y).include? user_input_check( - 'Should we include champion permanents in this process?', - $ans_yn, - $ans_yn_d - ) - loot_shards = - player_loot.select do |l| - l['type'] == 'CHAMPION_RENTAL' || l['type'] == 'CHAMPION' - end + loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } + if count_loot_items(loot_perms).positive? && (ans_y.include? user_input_check( + 'Should we include champion permanents in this process?', + ans_yn, + ans_yn_d + )) + loot_shards = + player_loot.select do |l| + l['type'] == 'CHAMPION_RENTAL' || l['type'] == 'CHAMPION' end - end + end - if count_loot_items(loot_shards) > 0 - puts "Found #{count_loot_items(loot_shards)} champion shards.".light_blue + if count_loot_items(loot_shards).positive? + puts "Found #{count_loot_items(loot_shards)} champion shards.".light_blue - loot_shards.each do |s| - s['count_keep'] = 0 - s['disenchant_note'] = '' - end - loot_shards_not_owned = - loot_shards.select { |s| s['redeemableStatus'] != 'ALREADY_OWNED' } + loot_shards.each do |s| + s['count_keep'] = 0 + s['disenchant_note'] = '' + end + loot_shards_not_owned = + loot_shards.select { |s| s['redeemableStatus'] != 'ALREADY_OWNED' } - if loot_shards_not_owned.length > 0 - if ($ans_y).include? user_input_check( - "Keep a shard for champions you don't own yet?", - $ans_yn, - $ans_yn_d - ) - loot_shards = handle_champions_owned(loot_shards) - end - else - puts "Found no shards of champions you don't own yet.".light_blue + if loot_shards_not_owned.length.positive? + if ans_y.include? user_input_check( + "Keep a shard for champions you don't own yet?", + ans_yn, + ans_yn_d + ) + loot_shards = handle_champions_owned(loot_shards) end + else + puts "Found no shards of champions you don't own yet.".light_blue + end - disenchant_modes = { - '1' => 'Disenchant all champion shards', - '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', - '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', - '4' => 'Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)', - '5' => 'Keep one shard of each champion regardless of mastery', - 'x' => 'Cancel', - } + disenchant_modes = { + '1' => 'Disenchant all champion shards', + '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', + '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', + '4' => 'Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)', + '5' => 'Keep one shard of each champion regardless of mastery', + 'x' => 'Cancel' + } - modes_string = '' - disenchant_modes.each do |k, v| - modes_string += "[#{k}] ".light_white - modes_string += "#{v}\n".light_cyan - end + modes_string = '' + disenchant_modes.each do |k, v| + modes_string += "[#{k}] ".light_white + modes_string += "#{v}\n".light_cyan + end - disenchant_shards_mode = - user_input_check( - "Okay, which option would you like to go by?\n" + modes_string + - 'Option: ', - disenchant_modes.keys, - '[1|2|3|4|5|x]', - '' - ) - unless disenchant_shards_mode == 'x' - case disenchant_shards_mode - when '1' - # no filtering needed -> done - when '2' - loot_shards = handle_champions_tokens(player_loot, loot_shards) - when '3' - loot_shards = handle_champions_mastery(loot_shards) - when '4' - loot_shards = handle_champions_mastery(loot_shards, true) - when '5' - loot_shards = handle_champions_collection(loot_shards) - end + disenchant_shards_mode = + user_input_check( + "Okay, which option would you like to go by?\n" + modes_string + + 'Option: ', + disenchant_modes.keys, + '[1|2|3|4|5|x]', + '' + ) + if disenchant_shards_mode == 'x' + puts 'Champion shard disenchanting canceled.'.yellow + else + case disenchant_shards_mode + when '1' + # no filtering needed -> done + when '2' + loot_shards = handle_champions_tokens(player_loot, loot_shards) + when '3' + loot_shards = handle_champions_mastery(loot_shards) + when '4' + loot_shards = handle_champions_mastery(loot_shards, true) + when '5' + loot_shards = handle_champions_collection(loot_shards) + end - loot_shards = loot_shards.select { |l| l['count'] > 0 } - loot_shards = - loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } + loot_shards = loot_shards.select { |l| l['count'].positive? } + loot_shards = + loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } - if count_loot_items(loot_shards) > 0 - puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue - loot_shards.each do |l| - loot_value = l['disenchantValue'] * l['count'] - print pad("#{l['count']}x ", 5, false).light_black - print pad("#{l['itemDesc']}", 15).light_white - print ' @ '.light_black - print pad("#{loot_value} BE", 8, false).light_black - if l['count_keep'] > 0 - puts " keeping #{l['count_keep']}".green - elsif l['disenchant_note'].length > 0 - puts " #{l['disenchant_note']}" - else - puts - end + if count_loot_items(loot_shards).positive? + puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue + loot_shards.each do |l| + loot_value = l['disenchantValue'] * l['count'] + print pad("#{l['count']}x ", 5, false).light_black + print pad(l['itemDesc'], 15).light_white + print ' @ '.light_black + print pad("#{loot_value} BE", 8, false).light_black + if l['count_keep'].positive? + puts " keeping #{l['count_keep']}".green + elsif l['disenchant_note'].length.positive? + puts " #{l['disenchant_note']}" + else + puts end + end - loot_shards = handle_champions_exceptions(loot_shards) + loot_shards = handle_champions_exceptions(loot_shards) - total_be_value = 0 - loot_shards.each do |l| - total_be_value += l['disenchantValue'] * l['count'] - end + total_be_value = 0 + loot_shards.each do |l| + total_be_value += l['disenchantValue'] * l['count'] + end - if count_loot_items(loot_shards) > 0 - if $ans_y.include? user_input_check( - "Disenchant #{count_loot_items(loot_shards)} champion shards for #{total_be_value} Blue Essence?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - $s_blue_essence += total_be_value - $s_disenchanted += count_loot_items(loot_shards) - threads = - loot_shards.map do |s| - Thread.new do - post_recipe( - 'CHAMPION_RENTAL_disenchant', - s['lootId'], - s['count'] - ) - end + if count_loot_items(loot_shards).positive? + if ans_y.include? user_input_check( + "Disenchant #{count_loot_items(loot_shards)} champion shards for #{total_be_value} Blue Essence?", + ans_yn, + ans_yn_d, + 'confirm' + ) + $s_blue_essence += total_be_value + $s_disenchanted += count_loot_items(loot_shards) + threads = + loot_shards.map do |s| + Thread.new do + post_recipe( + 'CHAMPION_RENTAL_disenchant', + s['lootId'], + s['count'] + ) end - threads.each(&:join) - puts 'Done!'.green - end - else - puts 'All remaining champions have been excluded, skipping...'.yellow + end + threads.each(&:join) + puts 'Done!'.green end else - puts "Job's already done: no champion shards left matching your selection.".green + puts 'All remaining champions have been excluded, skipping...'.yellow end else - puts 'Champion shard disenchanting canceled.'.yellow + puts "Job's already done: no champion shards left matching your selection.".green end - else - puts 'Found no champion shards to disenchant.'.yellow end - rescue => exception - handle_exception(exception, 'Champion Shards') + else + puts 'Found no champion shards to disenchant.'.yellow end +rescue StandardError => e + handle_exception(e, 'Champion Shards') end diff --git a/src/modules/handlers/champions/collection.rb b/src/modules/handlers/champions/collection.rb index df35e66..07c8d95 100644 --- a/src/modules/handlers/champions/collection.rb +++ b/src/modules/handlers/champions/collection.rb @@ -7,6 +7,6 @@ def handle_champions_collection(loot_shards) end loot_shards -rescue => e +rescue StandardError => e handle_exception(e, 'Champion Shards for Collection') end diff --git a/src/modules/handlers/champions/exceptions.rb b/src/modules/handlers/champions/exceptions.rb index 0b2c60f..a44acb7 100644 --- a/src/modules/handlers/champions/exceptions.rb +++ b/src/modules/handlers/champions/exceptions.rb @@ -1,41 +1,37 @@ # frozen_string_literal: true def handle_champions_exceptions(loot_shards) - begin - exclusions_str = '' - exclusions_done = false - exclusions_done_more = '' - exclusions_arr = [] - until exclusions_done - if ($ans_y).include? user_input_check( - "Would you like to add #{exclusions_done_more}exclusions?", - $ans_yn, - $ans_yn_d - ) - exclusions_str += - ',' + - ask( - 'Okay, which champions? '.light_cyan + - '(case-sensitive, comma-separated)'.light_white + - ': '.light_cyan - ) + exclusions_str = '' + exclusions_done = false + exclusions_done_more = '' + exclusions_arr = [] + until exclusions_done + if ans_y.include? user_input_check( + "Would you like to add #{exclusions_done_more}exclusions?", + ans_yn, + ans_yn_d + ) + exclusions_str += + ',' + + ask( + 'Okay, which champions? '.light_cyan + + '(case-sensitive, comma-separated)'.light_white + + ': '.light_cyan + ) - exclusions_done_more = 'more ' + exclusions_done_more = 'more ' - exclusions_arr = exclusions_str.split(/\s*,\s*/) - exclusions_matched = - loot_shards.select { |l| exclusions_arr.include? l['itemDesc'] } - print 'Exclusions recognized: '.green - exclusions_matched.each { |e| print e['itemDesc'].light_white + ' ' } - puts - else - exclusions_done = true - end + exclusions_arr = exclusions_str.split(/\s*,\s*/) + exclusions_matched = + loot_shards.select { |l| exclusions_arr.include? l['itemDesc'] } + print 'Exclusions recognized: '.green + exclusions_matched.each { |e| print e['itemDesc'].light_white + ' ' } + puts + else + exclusions_done = true end - loot_shards = - loot_shards.select { |l| !exclusions_arr.include? l['itemDesc'] } - return loot_shards - rescue => exception - handle_exception(exception, 'Champion Shard Exceptions') end + loot_shards.reject { |l| exclusions_arr.include? l['itemDesc'] } +rescue StandardError => e + handle_exception(e, 'Champion Shard Exceptions') end diff --git a/src/modules/handlers/champions/mastery.rb b/src/modules/handlers/champions/mastery.rb index 86cb83c..fe6d4b1 100644 --- a/src/modules/handlers/champions/mastery.rb +++ b/src/modules/handlers/champions/mastery.rb @@ -1,53 +1,50 @@ # frozen_string_literal: true def handle_champions_mastery(loot_shards, keep_all = false) - begin - summoner = get_current_summoner - player_mastery = get_champion_mastery(summoner['summonerId']) - threshold_champion_ids = [] - mastery6_champion_ids = [] - mastery7_champion_ids = [] + summoner = get_current_summoner + player_mastery = get_champion_mastery(summoner['summonerId']) + threshold_champion_ids = [] + mastery6_champion_ids = [] + mastery7_champion_ids = [] - unless keep_all - level_threshold = - user_input_check( - 'Which mastery level should champions at least be for their shards to be kept?', - %w[1 2 3 4 5 6], - '[1..6]' - ) - else - level_threshold = '0' - end - level_threshold = level_threshold.to_i + level_threshold = if keep_all + '0' + else + user_input_check( + 'Which mastery level should champions at least be for their shards to be kept?', + %w[1 2 3 4 5 6], + '[1..6]' + ) + end + level_threshold = level_threshold.to_i - player_mastery.each do |m| - if m['championLevel'] == 7 - mastery7_champion_ids << m['championId'] - elsif m['championLevel'] == 6 - mastery6_champion_ids << m['championId'] - elsif (level_threshold..5).include? m['championLevel'] - threshold_champion_ids << m['championId'] - elsif keep_all - threshold_champion_ids << m['championId'] - end + player_mastery.each do |m| + if m['championLevel'] == 7 + mastery7_champion_ids << m['championId'] + elsif m['championLevel'] == 6 + mastery6_champion_ids << m['championId'] + elsif (level_threshold..5).include? m['championLevel'] + threshold_champion_ids << m['championId'] + elsif keep_all + threshold_champion_ids << m['championId'] end + end - loot_shards.each do |l| - if mastery7_champion_ids.include? l['storeItemId'] - l['disenchant_note'] = 'at mastery 7'.light_black - elsif mastery6_champion_ids.include? l['storeItemId'] - l['count'] -= 1 - l['count_keep'] += 1 - elsif threshold_champion_ids.include? l['storeItemId'] - l['count'] -= 2 - l['count_keep'] += 2 - else - l['disenchant_note'] = 'below threshold'.yellow - end + loot_shards.each do |l| + if mastery7_champion_ids.include? l['storeItemId'] + l['disenchant_note'] = 'at mastery 7'.light_black + elsif mastery6_champion_ids.include? l['storeItemId'] + l['count'] -= 1 + l['count_keep'] += 1 + elsif threshold_champion_ids.include? l['storeItemId'] + l['count'] -= 2 + l['count_keep'] += 2 + else + l['disenchant_note'] = 'below threshold'.yellow end - - return loot_shards - rescue => exception - handle_exception(exception, 'Champion Shards by Mastery') end + + loot_shards +rescue StandardError => e + handle_exception(e, 'Champion Shards by Mastery') end diff --git a/src/modules/handlers/champions/owned.rb b/src/modules/handlers/champions/owned.rb index 562fb53..0f05975 100644 --- a/src/modules/handlers/champions/owned.rb +++ b/src/modules/handlers/champions/owned.rb @@ -1,15 +1,13 @@ # frozen_string_literal: true def handle_champions_owned(loot_shards) - begin - loot_shards.each do |l| - unless l['redeemableStatus'] == 'ALREADY_OWNED' - l['count'] -= 1 - l['count_keep'] += 1 - end + loot_shards.each do |l| + unless l['redeemableStatus'] == 'ALREADY_OWNED' + l['count'] -= 1 + l['count_keep'] += 1 end - return loot_shards.select { |l| l['count'] > 0 } - rescue => exception - handle_capsules(exception, 'Owned Champion Shards') end + loot_shards.select { |l| l['count'] > 0 } +rescue StandardError => e + handle_capsules(e, 'Owned Champion Shards') end diff --git a/src/modules/handlers/champions/tokens.rb b/src/modules/handlers/champions/tokens.rb index 2d98049..335f60c 100644 --- a/src/modules/handlers/champions/tokens.rb +++ b/src/modules/handlers/champions/tokens.rb @@ -1,35 +1,31 @@ # frozen_string_literal: true def handle_champions_tokens(player_loot, loot_shards) - begin - token6_champion_ids = [] - token7_champion_ids = [] + token6_champion_ids = [] + token7_champion_ids = [] - loot_mastery_tokens = - player_loot.select { |l| l['type'] == 'CHAMPION_TOKEN' } + loot_mastery_tokens = + player_loot.select { |l| l['type'] == 'CHAMPION_TOKEN' } - loot_mastery_tokens.each do |token| - if token['lootName'] = 'CHAMPION_TOKEN_6' - token6_champion_ids << token['refId'].to_i - elsif token['lootName'] = 'CHAMPION_TOKEN_7' - token7_champion_ids << token['refId'].to_i - end + loot_mastery_tokens.each do |token| + if token['lootName'] = 'CHAMPION_TOKEN_6' + token6_champion_ids << token['refId'].to_i + elsif token['lootName'] = 'CHAMPION_TOKEN_7' + token7_champion_ids << token['refId'].to_i end + end - puts "Found #{token6_champion_ids.length + token7_champion_ids.length} champions with owned mastery tokens".light_black + puts "Found #{token6_champion_ids.length + token7_champion_ids.length} champions with owned mastery tokens".light_black - loot_shards = - loot_shards.each do |l| - if token6_champion_ids.include? l['storeItemId'] - l['count'] -= 2 - l['count_keep'] += 2 - elsif token7_champion_ids.include? l['storeItemId'] - l['count'] -= 1 - l['count_keep'] += 1 - end - end - return loot_shards - rescue => exception - handle_exception(exception, 'Champion Shards by Tokens') + loot_shards.each do |l| + if token6_champion_ids.include? l['storeItemId'] + l['count'] -= 2 + l['count_keep'] += 2 + elsif token7_champion_ids.include? l['storeItemId'] + l['count'] -= 1 + l['count_keep'] += 1 + end end +rescue StandardError => e + handle_exception(e, 'Champion Shards by Tokens') end diff --git a/src/modules/handlers/generic.rb b/src/modules/handlers/generic.rb index bd95aa4..4b2d210 100644 --- a/src/modules/handlers/generic.rb +++ b/src/modules/handlers/generic.rb @@ -1,96 +1,90 @@ # frozen_string_literal: true def handle_generic(name, type, recipe) - begin - player_loot = get_player_loot - disenchant_all = true + player_loot = get_player_loot + disenchant_all = true - loot_generic = player_loot.select { |l| l['type'] == type } - if count_loot_items(loot_generic) > 0 - puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue + loot_generic = player_loot.select { |l| l['type'] == type } + if count_loot_items(loot_generic) > 0 + puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue - contains_unowned_items = false - loot_generic.each do |l| - if l['redeemableStatus'] != 'ALREADY_OWNED' - contains_unowned_items = true - end - end + contains_unowned_items = false + loot_generic.each do |l| + contains_unowned_items = true if l['redeemableStatus'] != 'ALREADY_OWNED' + end - if contains_unowned_items - user_option = - user_input_check( - "Keep #{name} you don't own yet?\n".light_cyan + - '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + - "No\n".light_cyan + '[x] '.light_white + - "Exit to main menu\n".light_cyan + 'Option: ', - %w[y n x], - '[y|n|x]', - '' - ) + if contains_unowned_items + user_option = + user_input_check( + "Keep #{name} you don't own yet?\n".light_cyan + + '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + + "No\n".light_cyan + '[x] '.light_white + + "Exit to main menu\n".light_cyan + 'Option: ', + %w[y n x], + '[y|n|x]', + '' + ) - case user_option - when 'x' - puts 'Action cancelled'.yellow - return - when 'y' - disenchant_all = false - loot_generic = - loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } - puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue - end + case user_option + when 'x' + puts 'Action cancelled'.yellow + return + when 'y' + disenchant_all = false + loot_generic = + loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } + puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue end + end - if count_loot_items(loot_generic) > 0 - total_oe_value = 0 - loot_generic.each do |g| - total_oe_value += g['disenchantValue'] * g['count'] - end + if count_loot_items(loot_generic) > 0 + total_oe_value = 0 + loot_generic.each do |g| + total_oe_value += g['disenchantValue'] * g['count'] + end - if loot_generic[0]['itemDesc'] == '' - loot_name_index = 'localizedName' - else - loot_name_index = 'itemDesc' + loot_name_index = if loot_generic[0]['itemDesc'] == '' + 'localizedName' + else + 'itemDesc' + end + loot_generic = + loot_generic.sort_by do |l| + [l['redeemableStatus'], l[loot_name_index]] end - loot_generic = - loot_generic.sort_by do |l| - [l['redeemableStatus'], l[loot_name_index]] - end - puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue - loot_generic.each do |l| - loot_value = l['disenchantValue'] * l['count'] - print pad("#{l['count']}x ", 5, false).light_black - print pad("#{l[loot_name_index]}", 30).light_white - print ' @ '.light_black - print pad("#{loot_value} OE", 8, false).light_black - if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' - print ' (not owned)'.yellow - end - puts - end + puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue + loot_generic.each do |l| + loot_value = l['disenchantValue'] * l['count'] + print pad("#{l['count']}x ", 5, false).light_black + print pad("#{l[loot_name_index]}", 30).light_white + print ' @ '.light_black + print pad("#{loot_value} OE", 8, false).light_black + print ' (not owned)'.yellow if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' + puts + end - if ($ans_y).include? user_input_check( - "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", - $ans_yn, - $ans_yn_d, - 'confirm' - ) - $s_disenchanted += count_loot_items(loot_generic) - $s_orange_essence += total_oe_value - threads = - loot_generic.map do |g| - Thread.new { post_recipe(recipe, g['lootId'], g['count']) } - end - threads.each(&:join) - puts 'Done!'.green - end - else - puts "Found no owned #{name} to disenchant.".yellow + if ans_y.include? user_input_check( + "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", + ans_yn, + ans_yn_d, + 'confirm' + ) + $s_disenchanted += count_loot_items(loot_generic) + $s_orange_essence += total_oe_value + threads = + loot_generic.map do |g| + Thread.new { post_recipe(recipe, g['lootId'], g['count']) } + end + threads.each(&:join) + puts 'Done!'.green end else - puts "Found no #{name} to disenchant.".yellow + puts "Found no owned #{name} to disenchant.".yellow end - rescue => exception - handle_exception(exception, name) + else + puts "Found no #{name} to disenchant.".yellow end +rescue StandardError => e + handle_exception(e, name) end diff --git a/src/modules/handlers/materials.rb b/src/modules/handlers/materials.rb index 74463db..435ca18 100644 --- a/src/modules/handlers/materials.rb +++ b/src/modules/handlers/materials.rb @@ -20,11 +20,11 @@ def handle_materials(client, stat_tracker) todo_string = '' things_todo.each do |k, v| todo_string += "[#{k}] ".light_white - unless things_done.include? k - todo_string += "#{v}\n".light_cyan - else - todo_string += "#{v} (done)\n".light_green - end + todo_string += if things_done.include? k + "#{v} (done)\n".light_green + else + "#{v}\n".light_cyan + end end todo = diff --git a/src/modules/handlers/materials/key_fragments.rb b/src/modules/handlers/materials/key_fragments.rb index 7a83396..cd35da3 100644 --- a/src/modules/handlers/materials/key_fragments.rb +++ b/src/modules/handlers/materials/key_fragments.rb @@ -28,6 +28,6 @@ def handle_key_fragments(client, stat_tracker) ) puts 'Done!'.green end -rescue => e +rescue StandardError => e handle_exception(e, 'Key Fragments') end diff --git a/src/modules/handlers/materials/mythic_essence.rb b/src/modules/handlers/materials/mythic_essence.rb index 8f73115..2e2158f 100644 --- a/src/modules/handlers/materials/mythic_essence.rb +++ b/src/modules/handlers/materials/mythic_essence.rb @@ -106,6 +106,6 @@ def handle_mythic_essence(client, stat_tracker) ) puts 'Done!'.green end -rescue => e +rescue StandardError => e handle_exception(e, 'Mythic Essence') end diff --git a/src/modules/loot_metainfo.rb b/src/modules/loot_metainfo.rb index 7003a6d..d1d1388 100644 --- a/src/modules/loot_metainfo.rb +++ b/src/modules/loot_metainfo.rb @@ -21,5 +21,5 @@ def get_chest_name(loot_id) return catalogue[loot_id] if catalogue.key?(loot_id) - return loot_id + loot_id end diff --git a/src/modules/update/checker.rb b/src/modules/update/checker.rb index 5d1c3a3..ef5721e 100644 --- a/src/modules/update/checker.rb +++ b/src/modules/update/checker.rb @@ -1,47 +1,45 @@ # frozen_string_literal: true def check_update(version_local) - begin - uri = - URI( - 'https://api.github.com/repos/marvinscham/disenchanter/releases/latest' - ) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Get.new(uri, "Content-Type": 'application/json') - res = http.request req - ans = JSON.parse(res.body) + uri = + URI( + 'https://api.github.com/repos/marvinscham/disenchanter/releases/latest' + ) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + req = Net::HTTP::Get.new(uri, 'Content-Type': 'application/json') + res = http.request req + ans = JSON.parse(res.body) - version_local = - Gem::Version.new(version_local.delete_prefix('v').delete_suffix('-beta')) - version_remote = - Gem::Version.new( - ans['tag_name'].delete_prefix('v').delete_suffix('-beta') - ) + version_local = + Gem::Version.new(version_local.delete_prefix('v').delete_suffix('-beta')) + version_remote = + Gem::Version.new( + ans['tag_name'].delete_prefix('v').delete_suffix('-beta') + ) - if version_remote > version_local - puts "New version #{ans['tag_name']} available!".light_yellow - if ($ans_y).include? user_input_check( - 'Would you like to download the new version now?', - $ans_yn, - $ans_yn_d - ) - `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans['tag_name']}/disenchanter_up.exe -L -o disenchanter_up.exe` - puts 'Done downloading!'.green + if version_remote > version_local + puts "New version #{ans['tag_name']} available!".light_yellow + if ans_y.include? user_input_check( + 'Would you like to download the new version now?', + ans_yn, + ans_yn_d + ) + `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans['tag_name']}/disenchanter_up.exe -L -o disenchanter_up.exe` + puts 'Done downloading!'.green - pid = spawn("start cmd.exe @cmd /k \"disenchanter_up.exe\"") - Process.detach(pid) - puts 'Exiting...'.light_black - exit - end - elsif version_local > version_remote - puts 'Welcome to the future!'.light_magenta - puts "Latest remote version: v#{version_remote}".light_blue - else - puts "You're up to date!".green + pid = spawn('start cmd.exe @cmd /k "disenchanter_up.exe"') + Process.detach(pid) + puts 'Exiting...'.light_black + exit end - rescue => exception - handle_exception(exception, 'self update') + elsif version_local > version_remote + puts 'Welcome to the future!'.light_magenta + puts "Latest remote version: v#{version_remote}".light_blue + else + puts "You're up to date!".green end +rescue StandardError => e + handle_exception(e, 'self update') end diff --git a/src/modules/user_input.rb b/src/modules/user_input.rb index c37b20e..10969f9 100644 --- a/src/modules/user_input.rb +++ b/src/modules/user_input.rb @@ -17,14 +17,14 @@ def user_input_check(question, answers, answerdisplay, color_preset = 'default') when 'default' question = "#{question} ".light_cyan + "#{answerdisplay}".light_white + - ': '.light_cyan + ': '.light_cyan end - until (answers).include? input + until answers.include? input input = ask question - unless (answers).include? input + unless answers.include? input puts 'Invalid answer, options: '.light_red + - "#{answerdisplay}".light_white + "#{answerdisplay}".light_white end end From 270faf108c60cd7039a9a0add403a8aa96e8f83c Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 20:29:33 +0100 Subject: [PATCH 36/63] Adjusted functions to new structure --- .github/workflows/ci.yml | 2 +- Gemfile | 2 +- Gemfile.lock | 13 +- src/class/client.rb | 7 +- src/main.rb | 6 +- src/modules/common_strings.rb | 4 + src/modules/detect_client.rb | 5 +- src/modules/handlers/champions.rb | 224 +++++++++--------- src/modules/handlers/champions/owned.rb | 4 +- src/modules/handlers/exception.rb | 2 +- src/modules/handlers/materials.rb | 10 +- src/modules/handlers/materials/capsules.rb | 28 ++- .../handlers/materials/key_fragments.rb | 4 +- .../handlers/materials/mastery_tokens.rb | 8 +- .../handlers/materials/mythic_essence.rb | 20 +- src/modules/loot_metainfo.rb | 4 +- 16 files changed, 171 insertions(+), 172 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65cfc17..bcdd309 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 3.1.2 + ruby-version: 3.2.3 bundler-cache: true - name: Install dependencies diff --git a/Gemfile b/Gemfile index 50c8181..95c5123 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' git_source(:github) { |_repo| 'https://github.com/#{repo}.git' } -ruby '3.1.2' +ruby '3.2.3' gem 'base64', '~> 0.1.1' gem 'colorize', '~> 0.8.1' diff --git a/Gemfile.lock b/Gemfile.lock index 08f53c5..0e45473 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,7 +20,6 @@ GEM parser (3.3.0.5) ast (~> 2.4.1) racc - prettier_print (1.2.1) public_suffix (5.0.4) racc (1.7.3) rainbow (3.1.1) @@ -39,16 +38,9 @@ GEM unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.30.0) parser (>= 3.2.1.0) - ruby-lsp (0.5.1) - language_server-protocol (~> 3.17.0) - sorbet-runtime - syntax_tree (>= 6.1.1, < 7) ruby-progressbar (1.13.0) rufo (0.17.0) - sorbet-runtime (0.5.11219) stringio (3.1.0) - syntax_tree (6.2.0) - prettier_print (>= 1.2.0) time (0.3.0) date unicode-display_width (2.5.0) @@ -68,12 +60,11 @@ DEPENDENCIES ocra (= 1.3.11) open-uri (~> 0.2.0) rubocop (~> 1.60) - ruby-lsp (~> 0.5.1) rufo (>= 0.13.0) win32-shortcut (~> 0.3.0) RUBY VERSION - ruby 3.1.2p20 + ruby 3.2.3p157 BUNDLED WITH - 2.3.15 + 2.4.19 diff --git a/src/class/client.rb b/src/class/client.rb index 5ecf9a1..1c29da2 100644 --- a/src/class/client.rb +++ b/src/class/client.rb @@ -4,12 +4,12 @@ # Holds port and token info class Client + attr_accessor :stat_tracker + def initialize(stat_tracker) begin @port, @token = grab_lockfile rescue StandardError - puts 'Could not grab session!'.light_red - puts 'Make sure the script is in your League Client folder and that your Client is running.'.light_red ask exit_string exit 1 end @@ -49,7 +49,8 @@ def request_get(path) end end - def request_post(path, body) + def request_post(path, _body) + puts "Posting against #{host}/#{path}".light_black if @debug # create_client do |http| # uri = URI("#{host}/#{path}") # req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') diff --git a/src/main.rb b/src/main.rb index cd254d9..04e0466 100644 --- a/src/main.rb +++ b/src/main.rb @@ -97,7 +97,7 @@ def run case todo when '1' - handle_materials(client, stat_tracker) + handle_materials(client) when '2' handle_champions(client) when '3' @@ -135,8 +135,4 @@ def run ask exit_string end -def pad(str, len, right: true) - format("%#{right ? '-' : ''}#{len}s", str) -end - run diff --git a/src/modules/common_strings.rb b/src/modules/common_strings.rb index 49a56de..329e624 100644 --- a/src/modules/common_strings.rb +++ b/src/modules/common_strings.rb @@ -23,3 +23,7 @@ def ans_n def ans_yn_d '[y|n]' end + +def pad(str, len, right: true) + format("%#{right ? '-' : ''}#{len}s", str) +end diff --git a/src/modules/detect_client.rb b/src/modules/detect_client.rb index 2c4bf27..104c9f8 100644 --- a/src/modules/detect_client.rb +++ b/src/modules/detect_client.rb @@ -49,8 +49,9 @@ def grab_lockfile begin contents = File.read(lockfile) rescue StandardError - puts 'Failed to automatically find your League Client path.'.light_red - puts 'Please place the script directly in your League Client folder.'.light_red + puts 'Failed to automatically find your League Client.'.light_red + puts 'Make sure your client is running and logged into your account.'.light_red + puts 'If it\'s running and you\'re seeing this, please place the script directly in your League Client folder.'.light_red end end diff --git a/src/modules/handlers/champions.rb b/src/modules/handlers/champions.rb index 7acb583..772a5d2 100644 --- a/src/modules/handlers/champions.rb +++ b/src/modules/handlers/champions.rb @@ -6,8 +6,8 @@ require_relative 'champions/owned' require_relative 'champions/tokens' -def handle_champions - player_loot = get_player_loot +def handle_champions(client) + player_loot = client.req_get_player_loot loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } @@ -22,126 +22,128 @@ def handle_champions end end - if count_loot_items(loot_shards).positive? - puts "Found #{count_loot_items(loot_shards)} champion shards.".light_blue + if count_loot_items(loot_shards).zero? + puts 'Found no champion shards to disenchant.'.yellow + return + end - loot_shards.each do |s| - s['count_keep'] = 0 - s['disenchant_note'] = '' - end - loot_shards_not_owned = - loot_shards.select { |s| s['redeemableStatus'] != 'ALREADY_OWNED' } - - if loot_shards_not_owned.length.positive? - if ans_y.include? user_input_check( - "Keep a shard for champions you don't own yet?", - ans_yn, - ans_yn_d - ) - loot_shards = handle_champions_owned(loot_shards) - end - else - puts "Found no shards of champions you don't own yet.".light_blue - end + puts "Found #{count_loot_items(loot_shards)} champion shards.".light_blue - disenchant_modes = { - '1' => 'Disenchant all champion shards', - '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', - '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', - '4' => 'Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)', - '5' => 'Keep one shard of each champion regardless of mastery', - 'x' => 'Cancel' - } - - modes_string = '' - disenchant_modes.each do |k, v| - modes_string += "[#{k}] ".light_white - modes_string += "#{v}\n".light_cyan - end + loot_shards.each do |s| + s['count_keep'] = 0 + s['disenchant_note'] = '' + end + loot_shards_not_owned = + loot_shards.reject { |s| s['redeemableStatus'] == 'ALREADY_OWNED' } + + if loot_shards_not_owned.empty? + puts "Found no shards of champions you don't own yet.".light_blue + elsif ans_y.include? user_input_check( + "Keep a shard for champions you don't own yet?", + ans_yn, + ans_yn_d + ) + loot_shards = handle_champions_owned(loot_shards) + end + + disenchant_modes = { + '1' => 'Disenchant all champion shards', + '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', + '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', + '4' => 'Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)', + '5' => 'Keep one shard of each champion regardless of mastery', + 'x' => 'Cancel' + } + + modes_string = '' + disenchant_modes.each do |k, v| + modes_string += "[#{k}] ".light_white + modes_string += "#{v}\n".light_cyan + end + + disenchant_shards_mode = + user_input_check( + "Okay, which option would you like to go by?\n#{modes_string}" \ + 'Option: ', + disenchant_modes.keys, + '[1|2|3|4|5|x]', + '' + ) + if disenchant_shards_mode == 'x' + puts 'Champion shard disenchanting canceled.'.yellow + return + end + + case disenchant_shards_mode + when '1' + # no filtering needed -> done + when '2' + loot_shards = handle_champions_tokens(player_loot, loot_shards) + when '3' + loot_shards = handle_champions_mastery(loot_shards) + when '4' + loot_shards = handle_champions_mastery(loot_shards, true) + when '5' + loot_shards = handle_champions_collection(loot_shards) + end + + loot_shards = loot_shards.select { |l| l['count'].positive? } + loot_shards = + loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } - disenchant_shards_mode = - user_input_check( - "Okay, which option would you like to go by?\n" + modes_string + - 'Option: ', - disenchant_modes.keys, - '[1|2|3|4|5|x]', - '' - ) - if disenchant_shards_mode == 'x' - puts 'Champion shard disenchanting canceled.'.yellow + if count_loot_items(loot_shards).zero? + puts "Job's already done: no champion shards left matching your selection.".green + return + end + + puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue + loot_shards.each do |l| + loot_value = l['disenchantValue'] * l['count'] + print pad("#{l['count']}x ", 5, false).light_black + print pad(l['itemDesc'], 15).light_white + print ' @ '.light_black + print pad("#{loot_value} BE", 8, false).light_black + if l['count_keep'].positive? + puts " keeping #{l['count_keep']}".green + elsif l['disenchant_note'].length.positive? + puts " #{l['disenchant_note']}" else - case disenchant_shards_mode - when '1' - # no filtering needed -> done - when '2' - loot_shards = handle_champions_tokens(player_loot, loot_shards) - when '3' - loot_shards = handle_champions_mastery(loot_shards) - when '4' - loot_shards = handle_champions_mastery(loot_shards, true) - when '5' - loot_shards = handle_champions_collection(loot_shards) - end + puts + end + end - loot_shards = loot_shards.select { |l| l['count'].positive? } - loot_shards = - loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } - - if count_loot_items(loot_shards).positive? - puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue - loot_shards.each do |l| - loot_value = l['disenchantValue'] * l['count'] - print pad("#{l['count']}x ", 5, false).light_black - print pad(l['itemDesc'], 15).light_white - print ' @ '.light_black - print pad("#{loot_value} BE", 8, false).light_black - if l['count_keep'].positive? - puts " keeping #{l['count_keep']}".green - elsif l['disenchant_note'].length.positive? - puts " #{l['disenchant_note']}" - else - puts - end - end + loot_shards = handle_champions_exceptions(loot_shards) - loot_shards = handle_champions_exceptions(loot_shards) + total_be_value = 0 + loot_shards.each do |l| + total_be_value += l['disenchantValue'] * l['count'] + end - total_be_value = 0 - loot_shards.each do |l| - total_be_value += l['disenchantValue'] * l['count'] - end + if count_loot_items(loot_shards).zero? + puts 'All remaining champions have been excluded, skipping...'.yellow + return + end - if count_loot_items(loot_shards).positive? - if ans_y.include? user_input_check( - "Disenchant #{count_loot_items(loot_shards)} champion shards for #{total_be_value} Blue Essence?", - ans_yn, - ans_yn_d, - 'confirm' + if ans_y.include? user_input_check( + "Disenchant #{count_loot_items(loot_shards)} champion shards for #{total_be_value} Blue Essence?", + ans_yn, + ans_yn_d, + 'confirm' + ) + client.stat_tracker.add_blue_essence(total_be_value) + client.stat_tracker.add_disenchanted(count_loot_items(loot_shards)) + threads = + loot_shards.map do |s| + Thread.new do + post_recipe( + 'CHAMPION_RENTAL_disenchant', + s['lootId'], + s['count'] ) - $s_blue_essence += total_be_value - $s_disenchanted += count_loot_items(loot_shards) - threads = - loot_shards.map do |s| - Thread.new do - post_recipe( - 'CHAMPION_RENTAL_disenchant', - s['lootId'], - s['count'] - ) - end - end - threads.each(&:join) - puts 'Done!'.green - end - else - puts 'All remaining champions have been excluded, skipping...'.yellow end - else - puts "Job's already done: no champion shards left matching your selection.".green end - end - else - puts 'Found no champion shards to disenchant.'.yellow + threads.each(&:join) + puts 'Done!'.green end rescue StandardError => e handle_exception(e, 'Champion Shards') diff --git a/src/modules/handlers/champions/owned.rb b/src/modules/handlers/champions/owned.rb index 0f05975..d46b579 100644 --- a/src/modules/handlers/champions/owned.rb +++ b/src/modules/handlers/champions/owned.rb @@ -7,7 +7,7 @@ def handle_champions_owned(loot_shards) l['count_keep'] += 1 end end - loot_shards.select { |l| l['count'] > 0 } + loot_shards.select { |l| l['count'].positive? } rescue StandardError => e - handle_capsules(e, 'Owned Champion Shards') + handle_exception(e, 'Owned Champion Shards') end diff --git a/src/modules/handlers/exception.rb b/src/modules/handlers/exception.rb index 89cfc7d..4ce372f 100644 --- a/src/modules/handlers/exception.rb +++ b/src/modules/handlers/exception.rb @@ -4,6 +4,6 @@ def handle_exception(exception, name) puts "An error occurred while handling #{name}.".light_red puts 'Please take a screenshot and create an issue at https://github.com/marvinscham/disenchanter/issues/new'.light_red puts "If you don't have a GitHub account, send it to dev@marvinscham.de".light_red - puts exception + puts "Exception Occurred #{exception.class}. Message: #{exception.message}. Backtrace: \n #{exception.backtrace.join("\n")}" puts 'Skipping this step...'.yellow end diff --git a/src/modules/handlers/materials.rb b/src/modules/handlers/materials.rb index 435ca18..8a6fd5c 100644 --- a/src/modules/handlers/materials.rb +++ b/src/modules/handlers/materials.rb @@ -5,7 +5,7 @@ require_relative 'materials/mastery_tokens' require_relative 'materials/mythic_essence' -def handle_materials(client, stat_tracker) +def handle_materials(client) done = false things_todo = { '1' => 'Mythic Essence', @@ -44,13 +44,13 @@ def handle_materials(client, stat_tracker) case todo when '1' - handle_mythic_essence(client, stat_tracker) + handle_mythic_essence(client) when '2' - handle_key_fragments(client, stat_tracker) + handle_key_fragments(client) when '3' - handle_capsules(client, stat_tracker) + handle_capsules(client) when '4' - handle_mastery_tokens(client, stat_tracker) + handle_mastery_tokens(client) when 'x' done = true end diff --git a/src/modules/handlers/materials/capsules.rb b/src/modules/handlers/materials/capsules.rb index 2ba0e3e..0693c97 100644 --- a/src/modules/handlers/materials/capsules.rb +++ b/src/modules/handlers/materials/capsules.rb @@ -1,62 +1,64 @@ # frozen_string_literal: true -def handle_capsules(client, stat_tracker) +def handle_capsules(client) player_loot = client.req_get_player_loot - loot_capsules = filter_key_capsules(player_loot) + loot_capsules = filter_key_capsules(client, player_loot) if count_loot_items(loot_capsules).zero? puts 'Found no keyless capsules to open.'.yellow return end - print_capsule_summary(loot_capsules) + print_capsule_summary(client, loot_capsules) if ans_y.include? user_input_check( "Open #{count_loot_items(loot_capsules)} (keyless) capsules?", ans_yn, ans_yn_d, 'confirm' ) - process_keyless_capsule_requests(client, stat_tracker) + process_keyless_capsule_requests(loot_capsules, client) puts 'Done!'.green end rescue StandardError => e handle_exception(e, 'Capsules') end -def filter_key_capsules(player_loot) +def filter_key_capsules(client, player_loot) loot_capsules = player_loot.select { |l| l['lootName'].start_with?('CHEST_') } loot_capsules.each do |c| - recipes = get_recipes_for_item(c['lootId']) + recipes = client.req_get_recipes_for_item(c['lootId']) c['needs_key'] = recipes[0]['slots'].length > 1 || !recipes[0]['type'] == 'OPEN' end - loot_capsules.select { |c| c['needs_key'] == false } + loot_capsules.reject { |c| c['needs_key'] } rescue StandardError => e handle_exception(e, 'Capsules: filtering loot') end -def process_keyless_capsule_requests(client, stat_tracker) +def process_keyless_capsule_requests(loot_capsules, client) threads = loot_capsules.map do |c| Thread.new do res = client.req_post_recipe("#{c['lootId']}_OPEN", c['lootId'], c['count']) - res['added'].each do |r| - stat_tracker.add_blue_essence(r['deltaCount']) if r['playerLoot']['lootId'] == 'CURRENCY_champion' + unless res.nil? + res['added'].each do |r| + client.stat_tracker.add_blue_essence(r['deltaCount']) if r['playerLoot']['lootId'] == 'CURRENCY_champion' + end end end end threads.each(&:join) - stat_tracker.add_opened(count_loot_items(loot_capsules)) + client.stat_tracker.add_opened(count_loot_items(loot_capsules)) rescue StandardError => e handle_exception(e, 'Capsules: request execution') end -def print_capsule_summary(loot_capsules) +def print_capsule_summary(client, loot_capsules) puts "Found #{count_loot_items(loot_capsules)} capsules:".light_blue loot_capsules.each do |c| - puts "#{c['count']}x ".light_black + get_chest_name(c['lootId']).light_white + puts "#{c['count']}x ".light_black + get_chest_name(client, c['lootId']).light_white end rescue StandardError => e handle_exception(e, 'Capsules: summary generation') diff --git a/src/modules/handlers/materials/key_fragments.rb b/src/modules/handlers/materials/key_fragments.rb index cd35da3..e9cf082 100644 --- a/src/modules/handlers/materials/key_fragments.rb +++ b/src/modules/handlers/materials/key_fragments.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -def handle_key_fragments(client, stat_tracker) +def handle_key_fragments(client) player_loot = client.req_get_player_loot loot_keys = @@ -20,7 +20,7 @@ def handle_key_fragments(client, stat_tracker) ans_yn_d, 'confirm' ) - stat_tracker.add_crafted(key_count) + client.stat_tracker.add_crafted(key_count) client.req_post_recipe( 'MATERIAL_key_fragment_forge', 'MATERIAL_key_fragment', diff --git a/src/modules/handlers/materials/mastery_tokens.rb b/src/modules/handlers/materials/mastery_tokens.rb index a0e6fba..340d2c6 100644 --- a/src/modules/handlers/materials/mastery_tokens.rb +++ b/src/modules/handlers/materials/mastery_tokens.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -def handle_mastery_tokens(client, stat_tracker) +def handle_mastery_tokens(client) player_loot = client.req_get_player_loot loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } - recipes6 = get_recipes_for_item('CHAMPION_TOKEN_6-1') - recipes7 = get_recipes_for_item('CHAMPION_TOKEN_7-1') + recipes6 = client.req_get_recipes_for_item('CHAMPION_TOKEN_6-1') + recipes7 = client.req_get_recipes_for_item('CHAMPION_TOKEN_7-1') recipe6_cost = recipes6.select do |r| r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' @@ -121,7 +121,7 @@ def handle_mastery_tokens(client, stat_tracker) ) end - stat_tracker.add_redeemed(1) + client.stat_tracker.add_redeemed(1) end end rescue StandardError => e diff --git a/src/modules/handlers/materials/mythic_essence.rb b/src/modules/handlers/materials/mythic_essence.rb index 2e2158f..8d8154b 100644 --- a/src/modules/handlers/materials/mythic_essence.rb +++ b/src/modules/handlers/materials/mythic_essence.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -def handle_mythic_essence(client, stat_tracker) +def handle_mythic_essence(client) player_loot = client.req_get_player_loot mythic_loot_id = 'CURRENCY_mythic' @@ -21,10 +21,11 @@ def handle_mythic_essence(client, stat_tracker) craft_mythic_type = user_input_check( - "Okay, what would you like to craft?\n" + - "[1] #{craft_mythic_type_names[0]}\n" + - "[2] #{craft_mythic_type_names[1]}\n" + - "[3] #{craft_mythic_type_names[2]}\n" + "[x] Cancel\n", + "Okay, what would you like to craft?\n" \ + "[1] #{craft_mythic_type_names[0]}\n" \ + "[2] #{craft_mythic_type_names[1]}\n" \ + "[3] #{craft_mythic_type_names[2]}\n" \ + "[x] Cancel\n", %w[1 2 3 x], '[1|2|3|x]' ) @@ -62,6 +63,7 @@ def handle_mythic_essence(client, stat_tracker) (1..loot_essence['count'].to_i) .to_a .append('all') + .append('x') .map!(&:to_s), "[1..#{loot_essence['count']}|all|x]" ) @@ -93,13 +95,13 @@ def handle_mythic_essence(client, stat_tracker) ) case craft_mythic_type when '1' - stat_tracker.add_blue_essence(craft_quantity) + client.stat_tracker.add_blue_essence(craft_quantity) when '2' - stat_tracker.add_orange_essence(craft_quantity) + client.stat_tracker.add_orange_essence(craft_quantity) end - stat_tracker.add_crafted(could_craft) + client.stat_tracker.add_crafted(could_craft) - post_recipe( + client.req_post_recipe( recipe['recipeName'], mythic_loot_id, (craft_mythic_amount / recipe['slots'][0]['quantity']).floor diff --git a/src/modules/loot_metainfo.rb b/src/modules/loot_metainfo.rb index d1d1388..43f0f08 100644 --- a/src/modules/loot_metainfo.rb +++ b/src/modules/loot_metainfo.rb @@ -8,8 +8,8 @@ def count_loot_items(loot_items) count end -def get_chest_name(loot_id) - chest_info = get_loot_info(loot_id) +def get_chest_name(client, loot_id) + chest_info = client.req_get_loot_info(loot_id) return chest_info['localizedName'] unless chest_info['localizedName'].empty? catalogue = { From 0e67c4ecb4ea0d1a3ee0f912581ae9c98f3e7f4b Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 21:55:36 +0100 Subject: [PATCH 37/63] Cleaned up Mastery handling --- src/modules/handlers/champions/mastery.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/modules/handlers/champions/mastery.rb b/src/modules/handlers/champions/mastery.rb index fe6d4b1..1665550 100644 --- a/src/modules/handlers/champions/mastery.rb +++ b/src/modules/handlers/champions/mastery.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -def handle_champions_mastery(loot_shards, keep_all = false) - summoner = get_current_summoner - player_mastery = get_champion_mastery(summoner['summonerId']) +def handle_champions_mastery(client, loot_shards, keep_all: false) + summoner = client.req_get_current_summoner + player_mastery = client.req_get_champion_mastery(summoner['summonerId']) threshold_champion_ids = [] mastery6_champion_ids = [] mastery7_champion_ids = [] @@ -23,9 +23,7 @@ def handle_champions_mastery(loot_shards, keep_all = false) mastery7_champion_ids << m['championId'] elsif m['championLevel'] == 6 mastery6_champion_ids << m['championId'] - elsif (level_threshold..5).include? m['championLevel'] - threshold_champion_ids << m['championId'] - elsif keep_all + elsif keep_all || ((level_threshold..5).include? m['championLevel']) threshold_champion_ids << m['championId'] end end From 34e2a1d76dc07a7cb0f219a00dd983d5868b8a42 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 22:12:08 +0100 Subject: [PATCH 38/63] No longer disenchants shards for champions at 0 mastery, fixes #87 --- src/modules/handlers/champions/mastery.rb | 27 +++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/modules/handlers/champions/mastery.rb b/src/modules/handlers/champions/mastery.rb index 1665550..f354c80 100644 --- a/src/modules/handlers/champions/mastery.rb +++ b/src/modules/handlers/champions/mastery.rb @@ -6,24 +6,23 @@ def handle_champions_mastery(client, loot_shards, keep_all: false) threshold_champion_ids = [] mastery6_champion_ids = [] mastery7_champion_ids = [] + level_threshold = 0 - level_threshold = if keep_all - '0' - else - user_input_check( - 'Which mastery level should champions at least be for their shards to be kept?', - %w[1 2 3 4 5 6], - '[1..6]' - ) - end - level_threshold = level_threshold.to_i + unless keep_all + level_threshold = user_input_check( + 'Which mastery level should champions at least be for their shards to be kept?', + %w[1 2 3 4 5 6], + '[1..6]' + ).to_i + end player_mastery.each do |m| - if m['championLevel'] == 7 + case m['championLevel'] + when 7 mastery7_champion_ids << m['championId'] - elsif m['championLevel'] == 6 + when 6 mastery6_champion_ids << m['championId'] - elsif keep_all || ((level_threshold..5).include? m['championLevel']) + when level_threshold..5 threshold_champion_ids << m['championId'] end end @@ -34,7 +33,7 @@ def handle_champions_mastery(client, loot_shards, keep_all: false) elsif mastery6_champion_ids.include? l['storeItemId'] l['count'] -= 1 l['count_keep'] += 1 - elsif threshold_champion_ids.include? l['storeItemId'] + elsif keep_all || (threshold_champion_ids.include? l['storeItemId']) l['count'] -= 2 l['count_keep'] += 2 else From 3f407976c76b12f37360d892e6f2c2bf89fb3648 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 22:13:01 +0100 Subject: [PATCH 39/63] Added exits for invalid states --- src/main.rb | 2 ++ src/modules/handlers/champions.rb | 13 ++++++++----- src/modules/handlers/materials/mythic_essence.rb | 7 +++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main.rb b/src/main.rb index 04e0466..abe3f55 100644 --- a/src/main.rb +++ b/src/main.rb @@ -120,6 +120,8 @@ def run handle_debug when 'x' done = true + else + puts 'Invalid state, exiting.'.yellow end client.refresh_loot puts separator diff --git a/src/modules/handlers/champions.rb b/src/modules/handlers/champions.rb index 772a5d2..bb43d3f 100644 --- a/src/modules/handlers/champions.rb +++ b/src/modules/handlers/champions.rb @@ -80,11 +80,14 @@ def handle_champions(client) when '2' loot_shards = handle_champions_tokens(player_loot, loot_shards) when '3' - loot_shards = handle_champions_mastery(loot_shards) + loot_shards = handle_champions_mastery(client, loot_shards) when '4' - loot_shards = handle_champions_mastery(loot_shards, true) + loot_shards = handle_champions_mastery(client, loot_shards, keep_all: true) when '5' loot_shards = handle_champions_collection(loot_shards) + else + puts 'Invalid state, exiting.'.yellow + return end loot_shards = loot_shards.select { |l| l['count'].positive? } @@ -99,10 +102,10 @@ def handle_champions(client) puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue loot_shards.each do |l| loot_value = l['disenchantValue'] * l['count'] - print pad("#{l['count']}x ", 5, false).light_black + print pad("#{l['count']}x ", 5, right: false).light_black print pad(l['itemDesc'], 15).light_white print ' @ '.light_black - print pad("#{loot_value} BE", 8, false).light_black + print pad("#{loot_value} BE", 8, right: false).light_black if l['count_keep'].positive? puts " keeping #{l['count_keep']}".green elsif l['disenchant_note'].length.positive? @@ -135,7 +138,7 @@ def handle_champions(client) threads = loot_shards.map do |s| Thread.new do - post_recipe( + client.req_post_recipe( 'CHAMPION_RENTAL_disenchant', s['lootId'], s['count'] diff --git a/src/modules/handlers/materials/mythic_essence.rb b/src/modules/handlers/materials/mythic_essence.rb index 8d8154b..c49fcca 100644 --- a/src/modules/handlers/materials/mythic_essence.rb +++ b/src/modules/handlers/materials/mythic_essence.rb @@ -41,6 +41,9 @@ def handle_mythic_essence(client) when 'x' puts 'Mythic crafting canceled.'.yellow return + else + puts 'Invalid state, exiting.'.yellow + return end recipes = client.req_get_recipes_for_item(mythic_loot_id) @@ -62,9 +65,9 @@ def handle_mythic_essence(client) "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]}?", (1..loot_essence['count'].to_i) .to_a + .map!(&:to_s) .append('all') - .append('x') - .map!(&:to_s), + .append('x'), "[1..#{loot_essence['count']}|all|x]" ) From 39df54fbb5131e6a5180306d92db12ec519db0ca Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 22:13:16 +0100 Subject: [PATCH 40/63] Updated Gemfile --- Gemfile | 8 +++++--- Gemfile.lock | 10 +++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 95c5123..dc8ca7a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,10 @@ +# frozen_string_literal: true source 'https://rubygems.org' -git_source(:github) { |_repo| 'https://github.com/#{repo}.git' } +git_source(:github) { |_repo| 'https://github.com/marvinscham/disenchanter.git' } ruby '3.2.3' -gem 'base64', '~> 0.1.1' -gem 'colorize', '~> 0.8.1' +gem 'base64', '~> 0.2' +gem 'colorize', '~> 1.1' gem 'json', '~> 2.6' gem 'launchy', '~> 2.5' gem 'open-uri', '~> 0.2.0' @@ -17,3 +18,4 @@ group :development do # Ruby linter, config in .rubocop gem 'rubocop', '~> 1.60', require: false end + diff --git a/Gemfile.lock b/Gemfile.lock index 0e45473..4ca517e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,8 +4,8 @@ GEM addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) - base64 (0.1.1) - colorize (0.8.1) + base64 (0.2.0) + colorize (1.1.0) date (3.3.4) json (2.7.1) language_server-protocol (3.17.0.3) @@ -39,7 +39,7 @@ GEM rubocop-ast (1.30.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) - rufo (0.17.0) + rufo (0.17.1) stringio (3.1.0) time (0.3.0) date @@ -53,8 +53,8 @@ PLATFORMS x86_64-linux DEPENDENCIES - base64 (~> 0.1.1) - colorize (~> 0.8.1) + base64 (~> 0.2) + colorize (~> 1.1) json (~> 2.6) launchy (~> 2.5) ocra (= 1.3.11) From e48f6fb32e491cc75253a42b36234dab0a99f26f Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 22:13:49 +0100 Subject: [PATCH 41/63] Various minor fixes --- src/modules/common_strings.rb | 14 ++++++++++++++ src/modules/handlers/champions/tokens.rb | 7 ++++--- src/modules/stat_submission.rb | 5 +---- src/modules/user_input.rb | 21 ++++++++------------- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/modules/common_strings.rb b/src/modules/common_strings.rb index 329e624..1b00250 100644 --- a/src/modules/common_strings.rb +++ b/src/modules/common_strings.rb @@ -27,3 +27,17 @@ def ans_yn_d def pad(str, len, right: true) format("%#{right ? '-' : ''}#{len}s", str) end + +unless String.method_defined?(:light_yellow) + # Type hints! + class String + def light_yellow = self + def light_blue = self + def light_green = self + def light_white = self + def light_black = self + def light_red = self + def light_magenta = self + def light_cyan = self + end +end diff --git a/src/modules/handlers/champions/tokens.rb b/src/modules/handlers/champions/tokens.rb index 335f60c..bfc38a8 100644 --- a/src/modules/handlers/champions/tokens.rb +++ b/src/modules/handlers/champions/tokens.rb @@ -8,14 +8,15 @@ def handle_champions_tokens(player_loot, loot_shards) player_loot.select { |l| l['type'] == 'CHAMPION_TOKEN' } loot_mastery_tokens.each do |token| - if token['lootName'] = 'CHAMPION_TOKEN_6' + if token['lootName'] == 'CHAMPION_TOKEN_6' token6_champion_ids << token['refId'].to_i - elsif token['lootName'] = 'CHAMPION_TOKEN_7' + elsif token['lootName'] == 'CHAMPION_TOKEN_7' token7_champion_ids << token['refId'].to_i end end - puts "Found #{token6_champion_ids.length + token7_champion_ids.length} champions with owned mastery tokens".light_black + token_champion_count = token6_champion_ids.length + token7_champion_ids.length + puts "Found #{token_champion_count} champions with owned mastery tokens".light_black loot_shards.each do |l| if token6_champion_ids.include? l['storeItemId'] diff --git a/src/modules/stat_submission.rb b/src/modules/stat_submission.rb index 23da6f8..6fa607d 100644 --- a/src/modules/stat_submission.rb +++ b/src/modules/stat_submission.rb @@ -42,15 +42,12 @@ def gather_stats(stat_tracker) stats = ['Actions', 'Disenchanted', 'Opened', 'Crafted', 'Redeemed', 'Blue Essence', 'Orange Essence'] out += stats.map { |stat| wrap_stat_line(stat, stat_tracker.send(stat.downcase.gsub(' ', '_'))) }.join - out end def wrap_stat_line(name, value) strlen = 15 numlen = 7 out = pad(name, strlen) - out += pad(value.to_s, numlen, false).light_white + out += pad(value.to_s, numlen, right: false).light_white out += "\n" - - out end diff --git a/src/modules/user_input.rb b/src/modules/user_input.rb index 10969f9..25154a9 100644 --- a/src/modules/user_input.rb +++ b/src/modules/user_input.rb @@ -1,31 +1,26 @@ # frozen_string_literal: true -def ask(q) - print(q) - q = gets - q.chomp +def ask(question) + print(question) + question = gets + question.chomp end -def user_input_check(question, answers, answerdisplay, color_preset = 'default') +def user_input_check(question, answers, answer_display, color_preset = 'default') input = '' case color_preset when 'confirm' question = - "CONFIRM: #{question} ".light_magenta + "#{answerdisplay}".light_white + - ': '.light_magenta + "CONFIRM: #{question} ".light_magenta + answer_display.to_s.light_white + ': '.light_magenta when 'default' question = - "#{question} ".light_cyan + "#{answerdisplay}".light_white + - ': '.light_cyan + "#{question} ".light_cyan + answer_display.to_s.light_white + ': '.light_cyan end until answers.include? input input = ask question - unless answers.include? input - puts 'Invalid answer, options: '.light_red + - "#{answerdisplay}".light_white - end + puts 'Invalid answer, options: '.light_red + answer_display.to_s.light_white unless answers.include? input end input From 03abbd0aba85f2c9d2221c153b34fd42619d4fed Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 22:50:44 +0100 Subject: [PATCH 42/63] Added docs + fixes --- src/class/client.rb | 11 +++++----- src/modules/handlers/champions.rb | 2 ++ src/modules/handlers/champions/collection.rb | 1 + src/modules/handlers/champions/exceptions.rb | 20 +++++++++++-------- src/modules/handlers/champions/mastery.rb | 4 ++++ src/modules/handlers/champions/owned.rb | 5 ++++- src/modules/handlers/champions/tokens.rb | 3 +++ src/modules/handlers/materials.rb | 3 +++ src/modules/handlers/materials/capsules.rb | 2 ++ .../handlers/materials/key_fragments.rb | 2 ++ .../handlers/materials/mastery_tokens.rb | 9 ++++++++- .../handlers/materials/mythic_essence.rb | 2 ++ 12 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/class/client.rb b/src/class/client.rb index 1c29da2..49e550b 100644 --- a/src/class/client.rb +++ b/src/class/client.rb @@ -5,6 +5,7 @@ # Holds port and token info class Client attr_accessor :stat_tracker + attr_reader :debug def initialize(stat_tracker) begin @@ -90,19 +91,19 @@ def req_post_recipe(recipe, loot_ids, repeat) loot_id_string = "[\"#{Array(loot_ids).join('", "')}\"]" - op = + post_answer = request_post( "lol-loot/v1/recipes/#{recipe}/craft?repeat=#{repeat}", loot_id_string ) - handle_post_debug - op + handle_post_debug(post_answer) + post_answer end - def handle_post_debug + def handle_post_debug(post_answer) return unless @debug - File.write('disenchanter_post.json', op.to_json) + File.write('disenchanter_post.json', post_answer.to_json) puts('Okay, written to disenchanter_post.json.') end end diff --git a/src/modules/handlers/champions.rb b/src/modules/handlers/champions.rb index bb43d3f..e162621 100644 --- a/src/modules/handlers/champions.rb +++ b/src/modules/handlers/champions.rb @@ -6,6 +6,8 @@ require_relative 'champions/owned' require_relative 'champions/tokens' +# Handles any champion shard and champion permanent related loot actions +# @param client Client connector def handle_champions(client) player_loot = client.req_get_player_loot loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } diff --git a/src/modules/handlers/champions/collection.rb b/src/modules/handlers/champions/collection.rb index 07c8d95..c403ec4 100644 --- a/src/modules/handlers/champions/collection.rb +++ b/src/modules/handlers/champions/collection.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# Will keep one shard for each champion def handle_champions_collection(loot_shards) loot_shards.each do |l| l['count'] -= 1 diff --git a/src/modules/handlers/champions/exceptions.rb b/src/modules/handlers/champions/exceptions.rb index a44acb7..41e77e8 100644 --- a/src/modules/handlers/champions/exceptions.rb +++ b/src/modules/handlers/champions/exceptions.rb @@ -1,36 +1,40 @@ # frozen_string_literal: true +# Allows adding exceptions to a previously made Champion Shard disenchantment selection +# @param loot_shards Loot array, champion shards and permanents only def handle_champions_exceptions(loot_shards) exclusions_str = '' exclusions_done = false exclusions_done_more = '' exclusions_arr = [] + until exclusions_done if ans_y.include? user_input_check( "Would you like to add #{exclusions_done_more}exclusions?", ans_yn, ans_yn_d ) - exclusions_str += - ',' + - ask( - 'Okay, which champions? '.light_cyan + - '(case-sensitive, comma-separated)'.light_white + - ': '.light_cyan - ) + exclusions_str += ',' + exclusions_str += ask( + 'Okay, which champions? '.light_cyan + + '(case-sensitive, comma-separated)'.light_white + + ': '.light_cyan + ) exclusions_done_more = 'more ' exclusions_arr = exclusions_str.split(/\s*,\s*/) exclusions_matched = loot_shards.select { |l| exclusions_arr.include? l['itemDesc'] } + print 'Exclusions recognized: '.green - exclusions_matched.each { |e| print e['itemDesc'].light_white + ' ' } + exclusions_matched.each { |e| print "#{e['itemDesc'].light_white} " } puts else exclusions_done = true end end + loot_shards.reject { |l| exclusions_arr.include? l['itemDesc'] } rescue StandardError => e handle_exception(e, 'Champion Shard Exceptions') diff --git a/src/modules/handlers/champions/mastery.rb b/src/modules/handlers/champions/mastery.rb index f354c80..7cde8dc 100644 --- a/src/modules/handlers/champions/mastery.rb +++ b/src/modules/handlers/champions/mastery.rb @@ -1,5 +1,9 @@ # frozen_string_literal: true +# Will keep shards to max out champions above a user-specified mastery level +# @param client Client connector +# @param loot_shards Loot array, pre-filtered to only champion shards and permanents +# @param keep_all Whether to keep all shards that could possibly be used for non-collection purposes def handle_champions_mastery(client, loot_shards, keep_all: false) summoner = client.req_get_current_summoner player_mastery = client.req_get_champion_mastery(summoner['summonerId']) diff --git a/src/modules/handlers/champions/owned.rb b/src/modules/handlers/champions/owned.rb index d46b579..24f9df8 100644 --- a/src/modules/handlers/champions/owned.rb +++ b/src/modules/handlers/champions/owned.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# Keeps a shard for each champion not owned yet +# @param loot_shards Loot array, pre-filtered to only champion shards and permanents def handle_champions_owned(loot_shards) loot_shards.each do |l| unless l['redeemableStatus'] == 'ALREADY_OWNED' @@ -7,7 +9,8 @@ def handle_champions_owned(loot_shards) l['count_keep'] += 1 end end - loot_shards.select { |l| l['count'].positive? } + + loot_shards rescue StandardError => e handle_exception(e, 'Owned Champion Shards') end diff --git a/src/modules/handlers/champions/tokens.rb b/src/modules/handlers/champions/tokens.rb index bfc38a8..ebb6307 100644 --- a/src/modules/handlers/champions/tokens.rb +++ b/src/modules/handlers/champions/tokens.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +# Keeps only shards to max out champions with owned mastery 6/7 tokens +# @param player_loot Loot array to grab mastery tokens +# @param loot_shards Loot array pre-filtered to only champion shards and permanents def handle_champions_tokens(player_loot, loot_shards) token6_champion_ids = [] token7_champion_ids = [] diff --git a/src/modules/handlers/materials.rb b/src/modules/handlers/materials.rb index 8a6fd5c..c138e79 100644 --- a/src/modules/handlers/materials.rb +++ b/src/modules/handlers/materials.rb @@ -5,6 +5,8 @@ require_relative 'materials/mastery_tokens' require_relative 'materials/mythic_essence' +# Handles anything from the 'materials' loot section like keys, capsules and more +# @param client Client connector def handle_materials(client) done = false things_todo = { @@ -54,6 +56,7 @@ def handle_materials(client) when 'x' done = true end + puts separator end end diff --git a/src/modules/handlers/materials/capsules.rb b/src/modules/handlers/materials/capsules.rb index 0693c97..3e03108 100644 --- a/src/modules/handlers/materials/capsules.rb +++ b/src/modules/handlers/materials/capsules.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# Opens keyless chests/capsules +# @param client Client connector def handle_capsules(client) player_loot = client.req_get_player_loot diff --git a/src/modules/handlers/materials/key_fragments.rb b/src/modules/handlers/materials/key_fragments.rb index e9cf082..faaaced 100644 --- a/src/modules/handlers/materials/key_fragments.rb +++ b/src/modules/handlers/materials/key_fragments.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# Combines key fragments to keys +# @param client Client connector def handle_key_fragments(client) player_loot = client.req_get_player_loot diff --git a/src/modules/handlers/materials/mastery_tokens.rb b/src/modules/handlers/materials/mastery_tokens.rb index 340d2c6..16543ce 100644 --- a/src/modules/handlers/materials/mastery_tokens.rb +++ b/src/modules/handlers/materials/mastery_tokens.rb @@ -1,12 +1,17 @@ # frozen_string_literal: true +# Redeems mastery 6/7 tokens as efficiently as possible: +# Prefers champion shard over champion permanent over blue essence +# @param client Client connector def handle_mastery_tokens(client) player_loot = client.req_get_player_loot + loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } recipes6 = client.req_get_recipes_for_item('CHAMPION_TOKEN_6-1') recipes7 = client.req_get_recipes_for_item('CHAMPION_TOKEN_7-1') + recipe6_cost = recipes6.select do |r| r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' @@ -52,8 +57,9 @@ def handle_mastery_tokens(client) print pad(t['itemDesc'], 15, false).light_white print ' to Mastery Level '.light_black - print (t['lootName'])[-1].light_white + print t['lootName'][-1].light_white print ' using '.light_black + if !ref_shard.empty? && ref_shard[0]['count'].positive? print 'a champion shard.'.green needed_shards += 1 @@ -68,6 +74,7 @@ def handle_mastery_tokens(client) needed_essence += recipe_cost t['upgrade_type'] = 'essence' end + puts end puts diff --git a/src/modules/handlers/materials/mythic_essence.rb b/src/modules/handlers/materials/mythic_essence.rb index c49fcca..c565a41 100644 --- a/src/modules/handlers/materials/mythic_essence.rb +++ b/src/modules/handlers/materials/mythic_essence.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# Handles mythic essence crafting +# @param client Client connector def handle_mythic_essence(client) player_loot = client.req_get_player_loot mythic_loot_id = 'CURRENCY_mythic' From b2892bf335bc76e8254dff4a84e86dde109210c0 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 23:18:38 +0100 Subject: [PATCH 43/63] Fixed debug mode --- src/class/client.rb | 1 + src/modules/debug.rb | 22 +++++++--------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/class/client.rb b/src/class/client.rb index 49e550b..e9bba07 100644 --- a/src/class/client.rb +++ b/src/class/client.rb @@ -7,6 +7,7 @@ class Client attr_accessor :stat_tracker attr_reader :debug + # @param stat_tracker StatTracker def initialize(stat_tracker) begin @port, @token = grab_lockfile diff --git a/src/modules/debug.rb b/src/modules/debug.rb index 59241c3..6ba9edf 100644 --- a/src/modules/debug.rb +++ b/src/modules/debug.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -def handle_debug +def handle_debug(client) done = false things_todo = { '1' => 'Write player_loot to file', @@ -25,43 +25,35 @@ def handle_debug todo = user_input_check( "\nWhat would you like to do?\n\n".light_cyan + todo_string + - 'Option: ', + 'Option: '.white, things_todo.keys, '', '' ) things_done << todo - puts $sep + puts separator puts puts "Option chosen: #{things_todo[todo]}".light_white case todo when '1' - player_loot = get_player_loot - + player_loot = client.req_get_player_loot File.write('disenchanter_loot.json', player_loot.to_json) - puts('Okay, written to disenchanter_loot.json.') when '2' loot_id = ask("Which lootId would you like the recipes for?\n".light_cyan) - - recipes = get_recipes_for_item loot_id - + recipes = client.req_get_recipes_for_item(loot_id) File.write('disenchanter_recipes.json', recipes.to_json) - puts('Okay, written to disenchanter_recipes.json.') when '3' loot_id = ask("Which lootId would you like the info for?\n".light_cyan) - - loot_info = get_loot_info loot_id - + loot_info = client.req_get_loot_info(loot_id) File.write('disenchanter_lootinfo.json', loot_info.to_json) - puts('Okay, written to disenchanter_lootinfo.json.') when 'm' - $debug = true + client.debug = true puts 'Debug mode enabled.' when 'x' done = true From 8c259be055a01d98d82cc4e18019932dbec59d70 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 23:18:56 +0100 Subject: [PATCH 44/63] Polished generic handler --- src/main.rb | 2 +- src/modules/handlers/generic.rb | 150 +++++++++++++++++--------------- src/modules/handlers/skins.rb | 8 +- 3 files changed, 84 insertions(+), 76 deletions(-) diff --git a/src/main.rb b/src/main.rb index abe3f55..0619894 100644 --- a/src/main.rb +++ b/src/main.rb @@ -117,7 +117,7 @@ def run when 'r' open_github when 'd' - handle_debug + handle_debug(client) when 'x' done = true else diff --git a/src/modules/handlers/generic.rb b/src/modules/handlers/generic.rb index 4b2d210..1c75d03 100644 --- a/src/modules/handlers/generic.rb +++ b/src/modules/handlers/generic.rb @@ -1,89 +1,95 @@ # frozen_string_literal: true -def handle_generic(name, type, recipe) - player_loot = get_player_loot +# Handles generic loot types +# @param client Client connector +# @param name Display name ("Skin Shards") +# @param type Riot loot type name ("SKIN_RENTAL") +# @param recipe Riot recipe name ("SKIN_RENTAL_disenchant") +def handle_generic(client, name, type, recipe) + player_loot = client.req_get_player_loot disenchant_all = true loot_generic = player_loot.select { |l| l['type'] == type } - if count_loot_items(loot_generic) > 0 - puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue + if count_loot_items(loot_generic).zero? + puts "Found no #{name} to disenchant.".yellow + return + end - contains_unowned_items = false - loot_generic.each do |l| - contains_unowned_items = true if l['redeemableStatus'] != 'ALREADY_OWNED' - end + puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue - if contains_unowned_items - user_option = - user_input_check( - "Keep #{name} you don't own yet?\n".light_cyan + - '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + - "No\n".light_cyan + '[x] '.light_white + - "Exit to main menu\n".light_cyan + 'Option: ', - %w[y n x], - '[y|n|x]', - '' - ) + contains_unowned_items = false + loot_generic.each do |l| + contains_unowned_items = true if l['redeemableStatus'] != 'ALREADY_OWNED' + end - case user_option - when 'x' - puts 'Action cancelled'.yellow - return - when 'y' - disenchant_all = false - loot_generic = - loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } - puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue - end + if contains_unowned_items + user_option = + user_input_check( + "Keep #{name} you don't own yet?\n".light_cyan + + '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + + "No\n".light_cyan + '[x] '.light_white + + "Exit to main menu\n".light_cyan + 'Option: '.white, + %w[y n x], + '[y|n|x]', + '' + ) + + case user_option + when 'x' + puts 'Action cancelled'.yellow + return + when 'y' + disenchant_all = false + loot_generic = + loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } + puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue end + end - if count_loot_items(loot_generic) > 0 - total_oe_value = 0 - loot_generic.each do |g| - total_oe_value += g['disenchantValue'] * g['count'] - end + if count_loot_items(loot_generic).zero? + puts "Found no owned #{name} to disenchant.".yellow + return + end - loot_name_index = if loot_generic[0]['itemDesc'] == '' - 'localizedName' - else - 'itemDesc' - end - loot_generic = - loot_generic.sort_by do |l| - [l['redeemableStatus'], l[loot_name_index]] - end + total_oe_value = 0 + loot_generic.each do |g| + total_oe_value += g['disenchantValue'] * g['count'] + end - puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue - loot_generic.each do |l| - loot_value = l['disenchantValue'] * l['count'] - print pad("#{l['count']}x ", 5, false).light_black - print pad("#{l[loot_name_index]}", 30).light_white - print ' @ '.light_black - print pad("#{loot_value} OE", 8, false).light_black - print ' (not owned)'.yellow if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' - puts - end + loot_name_index = 'itemDesc' + loot_name_index = 'localizedName' if loot_generic[0]['itemDesc'] == '' - if ans_y.include? user_input_check( - "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", - ans_yn, - ans_yn_d, - 'confirm' - ) - $s_disenchanted += count_loot_items(loot_generic) - $s_orange_essence += total_oe_value - threads = - loot_generic.map do |g| - Thread.new { post_recipe(recipe, g['lootId'], g['count']) } - end - threads.each(&:join) - puts 'Done!'.green - end - else - puts "Found no owned #{name} to disenchant.".yellow + loot_generic = + loot_generic.sort_by do |l| + [l['redeemableStatus'], l[loot_name_index]] end - else - puts "Found no #{name} to disenchant.".yellow + + puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue + loot_generic.each do |l| + loot_value = l['disenchantValue'] * l['count'] + print pad("#{l['count']}x ", 5, right: false).light_black + print pad(l[loot_name_index], 30).light_white + print ' @ '.light_black + print pad("#{loot_value} OE", 8, right: false).light_black + print ' (not owned)'.yellow if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' + puts + end + + if ans_y.include? user_input_check( + "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", + ans_yn, + ans_yn_d, + 'confirm' + ) + client.stat_tracker.add_disenchanted(count_loot_items(loot_generic)) + client.stat_tracker.add_orange_essence(total_oe_value) + threads = + loot_generic.map do |g| + Thread.new { client.req_post_recipe(recipe, g['lootId'], g['count']) } + end + threads.each(&:join) + + puts 'Done!'.green end rescue StandardError => e handle_exception(e, name) diff --git a/src/modules/handlers/skins.rb b/src/modules/handlers/skins.rb index ea38667..b91cf3a 100644 --- a/src/modules/handlers/skins.rb +++ b/src/modules/handlers/skins.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true -def handle_skins - handle_generic('Skin Shards', 'SKIN_RENTAL', 'SKIN_RENTAL_disenchant') - handle_generic('Skin Permanents', 'SKIN', 'SKIN_disenchant') +# Wrapper for skin shards and permanents +# @param client Client connector +def handle_skins(client) + handle_generic(client, 'Skin Shards', 'SKIN_RENTAL', 'SKIN_RENTAL_disenchant') + handle_generic(client, 'Skin Permanents', 'SKIN', 'SKIN_disenchant') end From 4a8763a5037e1fcb095f6f6739f54aa559c5e57f Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 23:19:12 +0100 Subject: [PATCH 45/63] Implemented tacticians, fixes #19 --- src/modules/handlers/tacticians.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/handlers/tacticians.rb b/src/modules/handlers/tacticians.rb index 10dba5c..6b7c6cd 100644 --- a/src/modules/handlers/tacticians.rb +++ b/src/modules/handlers/tacticians.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true -def handle_tacticians - puts 'Not yet supported, skipping'.light_black +# Wrapper for tacticians +# @param client Client connector +# @note There are no shards for tacticians, only permanents +def handle_tacticians(client) + handle_generic(client, 'Tacticians', 'COMPANION', 'COMPANION_disenchant') end From 7e159b11a8f26ac9be66ad6dba2a95a49dac4165 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 23:51:08 +0100 Subject: [PATCH 46/63] Simplified recipe handling, fixes #127 --- src/modules/handlers/champions.rb | 2 +- src/modules/handlers/emotes.rb | 7 +++++-- src/modules/handlers/eternals.rb | 12 +++++------- src/modules/handlers/generic.rb | 16 ++++++++++------ src/modules/handlers/icons.rb | 7 +++++-- src/modules/handlers/skins.rb | 4 ++-- src/modules/handlers/tacticians.rb | 2 +- src/modules/handlers/wards.rb | 12 +++++------- 8 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/modules/handlers/champions.rb b/src/modules/handlers/champions.rb index e162621..f0a377a 100644 --- a/src/modules/handlers/champions.rb +++ b/src/modules/handlers/champions.rb @@ -141,7 +141,7 @@ def handle_champions(client) loot_shards.map do |s| Thread.new do client.req_post_recipe( - 'CHAMPION_RENTAL_disenchant', + s['disenchantRecipeName'], s['lootId'], s['count'] ) diff --git a/src/modules/handlers/emotes.rb b/src/modules/handlers/emotes.rb index 6d1fb1c..2315c00 100644 --- a/src/modules/handlers/emotes.rb +++ b/src/modules/handlers/emotes.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true -def handle_emotes - handle_generic('Emotes', 'EMOTE', 'EMOTE_disenchant') +# Wrapper for emotes +# @param client Client connector +# @note No shards for emotes +def handle_emotes(client) + handle_generic(client, 'Emotes', 'EMOTE') end diff --git a/src/modules/handlers/eternals.rb b/src/modules/handlers/eternals.rb index c931a48..07075a5 100644 --- a/src/modules/handlers/eternals.rb +++ b/src/modules/handlers/eternals.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true -def handle_eternals - handle_generic( - 'Eternal Shards', - 'STATSTONE_SHARD', - 'STATSTONE_SHARD_DISENCHANT' - ) - handle_generic('Eternals', 'STATSTONE', 'STATSTONE_DISENCHANT') +# Wrapper for eternals sets and their shards +# @param client Client connector +def handle_eternals(client) + handle_generic(client, 'Eternals Set Shards', 'STATSTONE_SHARD') + handle_generic(client, 'Eternals Set Permanent', 'STATSTONE') end diff --git a/src/modules/handlers/generic.rb b/src/modules/handlers/generic.rb index 1c75d03..b2300d9 100644 --- a/src/modules/handlers/generic.rb +++ b/src/modules/handlers/generic.rb @@ -4,8 +4,7 @@ # @param client Client connector # @param name Display name ("Skin Shards") # @param type Riot loot type name ("SKIN_RENTAL") -# @param recipe Riot recipe name ("SKIN_RENTAL_disenchant") -def handle_generic(client, name, type, recipe) +def handle_generic(client, name, type) player_loot = client.req_get_player_loot disenchant_all = true @@ -52,8 +51,10 @@ def handle_generic(client, name, type, recipe) end total_oe_value = 0 + total_be_value = 0 loot_generic.each do |g| - total_oe_value += g['disenchantValue'] * g['count'] + total_be_value += g['disenchantValue'] * g['count'] if g['disenchantLootName'] == 'CURRENCY_champion' + total_oe_value += g['disenchantValue'] * g['count'] if g['disenchantLootName'] == 'CURRENCY_cosmetic' end loot_name_index = 'itemDesc' @@ -67,25 +68,28 @@ def handle_generic(client, name, type, recipe) puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue loot_generic.each do |l| loot_value = l['disenchantValue'] * l['count'] + loot_currency = l['disenchantLootName'] == 'CURRENCY_champion' ? 'BE' : 'OE' + print pad("#{l['count']}x ", 5, right: false).light_black print pad(l[loot_name_index], 30).light_white print ' @ '.light_black - print pad("#{loot_value} OE", 8, right: false).light_black + print pad("#{loot_value} #{loot_currency}", 8, right: false).light_black print ' (not owned)'.yellow if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' puts end if ans_y.include? user_input_check( - "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence?", + "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence and #{total_be_value} Blue Essence?", ans_yn, ans_yn_d, 'confirm' ) client.stat_tracker.add_disenchanted(count_loot_items(loot_generic)) + client.stat_tracker.add_blue_essence(total_be_value) client.stat_tracker.add_orange_essence(total_oe_value) threads = loot_generic.map do |g| - Thread.new { client.req_post_recipe(recipe, g['lootId'], g['count']) } + Thread.new { client.req_post_recipe(g['disenchantRecipeName'], g['lootId'], g['count']) } end threads.each(&:join) diff --git a/src/modules/handlers/icons.rb b/src/modules/handlers/icons.rb index 2aab83d..0460f7b 100644 --- a/src/modules/handlers/icons.rb +++ b/src/modules/handlers/icons.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true -def handle_icons - handle_generic('Icons', 'SUMMONERICON', 'SUMMONERICON_disenchant') +# Wrapper for summoner icons +# @param client Client connector +# @note No shards for icons! +def handle_icons(client) + handle_generic(client, 'Icons', 'SUMMONERICON') end diff --git a/src/modules/handlers/skins.rb b/src/modules/handlers/skins.rb index b91cf3a..2490df7 100644 --- a/src/modules/handlers/skins.rb +++ b/src/modules/handlers/skins.rb @@ -3,6 +3,6 @@ # Wrapper for skin shards and permanents # @param client Client connector def handle_skins(client) - handle_generic(client, 'Skin Shards', 'SKIN_RENTAL', 'SKIN_RENTAL_disenchant') - handle_generic(client, 'Skin Permanents', 'SKIN', 'SKIN_disenchant') + handle_generic(client, 'Skin Shards', 'SKIN_RENTAL') + handle_generic(client, 'Skin Permanents', 'SKIN') end diff --git a/src/modules/handlers/tacticians.rb b/src/modules/handlers/tacticians.rb index 6b7c6cd..741e05c 100644 --- a/src/modules/handlers/tacticians.rb +++ b/src/modules/handlers/tacticians.rb @@ -4,5 +4,5 @@ # @param client Client connector # @note There are no shards for tacticians, only permanents def handle_tacticians(client) - handle_generic(client, 'Tacticians', 'COMPANION', 'COMPANION_disenchant') + handle_generic(client, 'Tacticians', 'COMPANION') end diff --git a/src/modules/handlers/wards.rb b/src/modules/handlers/wards.rb index 48ff07a..5c4c598 100644 --- a/src/modules/handlers/wards.rb +++ b/src/modules/handlers/wards.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true -def handle_ward_skins - handle_generic( - 'Ward Skin Shards', - 'WARDSKIN_RENTAL', - 'WARDSKIN_RENTAL_disenchant' - ) - handle_generic('Ward Skin Permanents', 'WARDSKIN', 'WARDSKIN_disenchant') +# Wrapper for ward skins and their permanents +# @param client Client connector +def handle_ward_skins(client) + handle_generic(client, 'Ward Skin Shards', 'WARDSKIN_RENTAL') + handle_generic(client, 'Ward Skin Permanents', 'WARDSKIN') end From df750d263c76c8c52e2170cb4c480ec037f60ac9 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Thu, 15 Feb 2024 23:51:27 +0100 Subject: [PATCH 47/63] Made entering debug mode possible again --- src/class/client.rb | 3 +-- src/modules/debug.rb | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/class/client.rb b/src/class/client.rb index e9bba07..96ddee6 100644 --- a/src/class/client.rb +++ b/src/class/client.rb @@ -4,8 +4,7 @@ # Holds port and token info class Client - attr_accessor :stat_tracker - attr_reader :debug + attr_accessor :stat_tracker, :debug # @param stat_tracker StatTracker def initialize(stat_tracker) diff --git a/src/modules/debug.rb b/src/modules/debug.rb index 6ba9edf..88d547f 100644 --- a/src/modules/debug.rb +++ b/src/modules/debug.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# Handles debug related stuff +# @param client Client connector def handle_debug(client) done = false things_todo = { From 382ccd900f149b80d022ea438e3b5d14c461d649 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Fri, 16 Feb 2024 00:26:08 +0100 Subject: [PATCH 48/63] Added filter for non-disenchantables + esports icon re-roll (fixes #57) --- src/modules/handlers/emotes.rb | 37 ++++++++++++++++++++++++++++++++- src/modules/handlers/generic.rb | 12 ++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/modules/handlers/emotes.rb b/src/modules/handlers/emotes.rb index 2315c00..bc46cfc 100644 --- a/src/modules/handlers/emotes.rb +++ b/src/modules/handlers/emotes.rb @@ -1,8 +1,43 @@ # frozen_string_literal: true -# Wrapper for emotes +# Wrapper for emotes, also handles non-disenchantable esports emotes # @param client Client connector # @note No shards for emotes def handle_emotes(client) handle_generic(client, 'Emotes', 'EMOTE') + client.refresh_loot + + player_loot = client.req_get_player_loot + esports_emotes = player_loot.select do |l| + l['type'] == 'EMOTE' \ + && l['disenchantLootName'] != '' \ + && l['redeemableStatus'] == 'ALREADY_OWNED' + end + if count_loot_items(esports_emotes).zero? + puts 'Found no Esports Emotes to re-roll.'.yellow + return + end + + puts "Found #{count_loot_items(esports_emotes)} Esports Emotes." + if ans_y.include? user_input_check( + "Re-roll #{count_loot_items(esports_emotes)} already owned Esports Emotes?", + ans_yn, + ans_yn_d, + 'confirm' + ) + client.stat_tracker.add_crafted(count_loot_items(esports_emotes)) + threads = + esports_emotes.map do |g| + Thread.new { client.req_post_recipe('EMOTE_forge', g['lootId'], g['count']) } + end + threads.each(&:join) + + puts 'Done!'.green + client.refresh_loot + else + return + end + + # Re-handle icons in case new disenchant candidates came up + handle_generic(client, 'Emotes', 'EMOTE') end diff --git a/src/modules/handlers/generic.rb b/src/modules/handlers/generic.rb index b2300d9..752adef 100644 --- a/src/modules/handlers/generic.rb +++ b/src/modules/handlers/generic.rb @@ -9,6 +9,8 @@ def handle_generic(client, name, type) disenchant_all = true loot_generic = player_loot.select { |l| l['type'] == type } + # Things like esports icons cannot be disenchanted -> drop + loot_generic = loot_generic.reject { |l| l['disenchantLootName'] == '' } if count_loot_items(loot_generic).zero? puts "Found no #{name} to disenchant.".yellow return @@ -39,8 +41,7 @@ def handle_generic(client, name, type) return when 'y' disenchant_all = false - loot_generic = - loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } + loot_generic = loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue end end @@ -78,8 +79,13 @@ def handle_generic(client, name, type) puts end + disenchant_info = '' + disenchant_info += "#{total_oe_value} Orange Essence" if total_oe_value.positive? + disenchant_info += ' and ' if total_oe_value.positive? && total_be_value.positive? + disenchant_info += "#{total_be_value} Blue Essence" if total_be_value.positive? + if ans_y.include? user_input_check( - "Disenchant #{count_loot_items(loot_generic)} #{name} for #{total_oe_value} Orange Essence and #{total_be_value} Blue Essence?", + "Disenchant #{count_loot_items(loot_generic)} #{name} for #{disenchant_info}?", ans_yn, ans_yn_d, 'confirm' From 7ca8d6d17612f24d654e9f02169cd648eeb01988 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Fri, 16 Feb 2024 00:26:31 +0100 Subject: [PATCH 49/63] Removed debug output files from tracking --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5f88fcd..35eebe5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ disenchanter_up.exe tmpin tmpout +disenchanter_*.json settings.json rubocop-report.json From 4f6b58a052eb2b40108898a034bd3fcfa7a7edf8 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Fri, 16 Feb 2024 02:23:36 +0100 Subject: [PATCH 50/63] Fixed local debug condition --- src/modules/handlers/emotes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/handlers/emotes.rb b/src/modules/handlers/emotes.rb index bc46cfc..f8cbfac 100644 --- a/src/modules/handlers/emotes.rb +++ b/src/modules/handlers/emotes.rb @@ -10,7 +10,7 @@ def handle_emotes(client) player_loot = client.req_get_player_loot esports_emotes = player_loot.select do |l| l['type'] == 'EMOTE' \ - && l['disenchantLootName'] != '' \ + && l['disenchantLootName'] == '' \ && l['redeemableStatus'] == 'ALREADY_OWNED' end if count_loot_items(esports_emotes).zero? From 7d247ef042df0c89ba56a1580f5a72c1a71015c1 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Fri, 16 Feb 2024 03:20:48 +0100 Subject: [PATCH 51/63] Added changelog and versioning docs --- CHANGELOG.md | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ VERSIONING.md | 14 +++++++ 2 files changed, 119 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 VERSIONING.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7200192 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,105 @@ +# Changelog + +All notable changes to this project will be documented in this file. +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Also see the [versioning strategy](./VERSIONING.md). + +## v1.6.0 - next +### Added +- #57 Rerolling owned esports emotes that cannot be disenchanted +- #19 Tacticians can now be disenchanted +- #27 Proper changelog and versioning files +### Changed +- (dev) Code is split in modules now instead of stuffed into a single script +### Fixed +- #142 Script won't crash if you don't have a summoner name + - Essentially Riot ID support +- #87 Shards of champions with no mastery will no longer falsely be considered for disenchanting +- #127 Champion permanents can be disenchanted again +- Reliability improvements via more flexible recipe and currency detection + - #128 Allows properly disenchanting things like blue essence granting summoner icons + +## v1.5.0 - Jul 26, 2022 +### Added +- Support for disenchanting champion, skin, ward skin and eternal permanents +- Mastery token upgrades will also use champion permanents +- Debug options for troubleshooting +- (dev) Linting and formatting rules +- Demo image in `README` :) +### Changed +- Smarter detection for items that aren't owned yet +- Blue essence directly looted from capsules is now included in stats +- Loot will be refreshed more frequently to prevent calls on stale data +### Fixed +- Clarity when upgrading mastery tokens +- Some chest display names like Honor Capsules will have the name manually injected instead +- Faulty crafting recipes adjusted +- Typos removed + +## v1.4.0 - Jul 14, 2022 +### Added +- Disenchanting summoner icons +- Efficient mastery 6/7 token upgrades + - Does not support champion permanents (yet) +### Changed +- Menu order is now like in the Client's loot tab + - Materials are in separate submenu now +### Fixed +- #21 Bugfix for event token crafting +- Smarter version check for people directly running the Ruby script +- Code cleanup, minor bugfixes + +## v1.3.2 - Jun 30, 2022 +### Fixed +- #10 Collection feature properly retains one shard per champion + +## v1.3.1 - Jun 29, 2022 +### Fixed +- Fixed "keep shards of champions you don't own yet" option +- Fixed in-place updater + added backwards compatibility + +## v1.3.0 - Jun 28, 2022 +### Added +- In-place updating + - Future versions can replace the old script with the latest version +- More things to disenchant: + - Ward skins + - Skins + - Eternals +### Changed +- Swapped to new, less bloated menu style +### Fixed +- Reliability improvements to existing options + +## v1.2.3 - Jun 21, 2022 +### Fixed +- Clarity in wording and visuals +- Fixes to conservative options + +## v1.2.2 - Jun 21, 2022 +### Fixed +- Failing one step will no longer break the script but fall back to the main menu + +## v1.2.1 - Jun 19, 2022 +### Fixed +- Bugfix for capsule handling +### Docs +- Added info on malware false positives + +## v1.2.0 - Jun 19, 2022 +### Added +- Mythic Essence crafting to random skin shards, blue essence and orange essence +- Opening of keyless capsules +- Colors in terminal +- User can now specify how many event tokens should be used + +## v1.1.1 - Jun 18, 2022 +### Fixed +- Bugfix in stat submission + +## v1.1.0 - Jun 17, 2022 +### Added +- Will notify the user whether the latest version is running + +## v1.0.0 - Jun 17, 2022 +### Added +- Initial Release \ No newline at end of file diff --git a/VERSIONING.md b/VERSIONING.md new file mode 100644 index 0000000..79d2120 --- /dev/null +++ b/VERSIONING.md @@ -0,0 +1,14 @@ +# Versioning + +Disenchanter's versioning is based on [Semantic Versioning](https://semver.org/). + +The version number is structured as `MAJOR.MINOR.PATCH`: +- `MAJOR` version is incremented on changes breaking the current usage flow +- `MINOR` version is incremented on backwards compatible functionality additions +- `PATCH` version is incremented on backwards compatible bug fixes + +Notable changes in documentation will be documented in the [changelog](./CHANGELOG.md) for historical insights. + +## Versioning Workflow + +Versioning is handled via [bumpversion](https://github.com/peritus/bumpversion), configured in `.bumpversion.cfg`. \ No newline at end of file From 8ab6aa6a8b2ceb5d99eb7c44ce657b3378b02cbd Mon Sep 17 00:00:00 2001 From: marvinscham Date: Fri, 16 Feb 2024 03:37:49 +0100 Subject: [PATCH 52/63] Updated docs, moved assets to folder --- README.md | 33 +++++--------------- BE_icon.ico => assets/BE_icon.ico | Bin disenchanter.png => assets/disenchanter.png | Bin assets/kofi-button.png | Bin 0 -> 8141 bytes build.cmd | 6 ---- scripts/build_main.sh | 2 +- 6 files changed, 9 insertions(+), 32 deletions(-) rename BE_icon.ico => assets/BE_icon.ico (100%) rename disenchanter.png => assets/disenchanter.png (100%) create mode 100644 assets/kofi-button.png delete mode 100644 build.cmd diff --git a/README.md b/README.md index d5a4606..c6cc1f9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

Disenchanter

@@ -19,7 +19,7 @@ Mass disenchant LoL loot like champion shards, eternals, mythic essence and more! -[](https://www.buymeacoffee.com/mscham) +[](https://www.buymeacoffee.com/mscham) Based on [Anujan/disenchant-champ-shards](https://github.com/Anujan/disenchant-champ-shards). @@ -31,13 +31,15 @@ Based on [Anujan/disenchant-champ-shards](https://github.com/Anujan/disenchant-c Download the pre-built `disenchanter.exe` from the [Latest Release](https://github.com/marvinscham/disenchanter/releases). -Put the `disenchanter.exe` file in the same folder as your `LeagueClient.exe`, e.g. `C:\Riot Games\League of Legends` and run it. Make sure your League Client is running without admin privileges and you're logged in before running Disenchanter. +Start your League Client **without admin privileges** and log into your account, then start the script. + +## Details The script is interactive and will guide you through the process with simple `[y|n]` questions and mode options. Before you disenchant or craft anything, you will be asked to confirm the action in a magenta colored message with a big `CONFIRM:` banner so don't be scared to explore the different options! Once you're finished, you can _optionally_ contribute your (anonymous) stats to the [Global Stats](https://github.com/marvinscham/disenchanter/wiki/Stats). ([Details](https://github.com/marvinscham/disenchanter/wiki/Stat-Collection)) -![Demo](https://raw.githubusercontent.com/marvinscham/disenchanter/main/disenchanter.png) +![Demo](./assets/disenchanter.png) ## Is this a virus? @@ -51,43 +53,26 @@ The script triggers the same server requests as you would in your League Client. ## Features -- _Note: no longer supports event tokens since Riot updated event passes_ - Materials - - Craft Mythic Essence to Skins or Blue/Orange Essence - - Combine Key Fragments - - Open keyless capsules - - - Upgrade Mastery Tokens - + - Upgrade Mastery Tokens efficiently - Champion Shards - - Disenchant all - - Keep one for champions you don't own yet - - Keep enough (1/2) for champions you own mastery 6/7 tokens for - - Keep enough (1/2) to fully master champions at least at mastery level x (select from 1 to 6) - - Keep enough (1/2) to fully master all champions (only disenchant shards that have no possible use) - - Keep one of each champion regardless of mastery - - Manual exceptions - - Disenchant various items - - Eternals - - Emotes - - Ward Skins - - Summoner Icons + - Tacticians ## Problems, Bugs and Feature Suggestions @@ -95,13 +80,11 @@ Something isn't working properly or you'd like to see a feature that isn't yet s - [Create an issue](https://github.com/marvinscham/disenchanter/issues/new/choose) - (**If you have no GitHub account**) hit me up at dev[at]marvinscham.de - - Open a pull request with your contribution. ## ❤ Sponsors ❤ - Ze Interrupter - - tsunamihorseracing ## Disclaimer diff --git a/BE_icon.ico b/assets/BE_icon.ico similarity index 100% rename from BE_icon.ico rename to assets/BE_icon.ico diff --git a/disenchanter.png b/assets/disenchanter.png similarity index 100% rename from disenchanter.png rename to assets/disenchanter.png diff --git a/assets/kofi-button.png b/assets/kofi-button.png new file mode 100644 index 0000000000000000000000000000000000000000..9982fb5ac2b48c3920f589acdb37fc3c992718f0 GIT binary patch literal 8141 zcmZWu1x(yglV6||cPj-7g;FT)P~0gLSlpfBQrvZMr^VfhyIavk3oOuu;!evVWwGM$ zKfc^ua>*t0ChtvtBQKeGl8I4MRlvi3i46jQ@DxADYJfl}7f*9lOtdGy#X(970-=G_ zl(pnYpuiIm4+6wb{D0|5qHO2-ANl{G|G)f?WavNY|4jh!KX#s6kU{}+C_n}Up7jA( z{lM)5fYA?73;~aiPnbu*#PI$RAQ*ho0PgSai3c7Y9)arz0MrYh_5wE#z}@2$@{wc$ zc-{v*Lq{j>zsDT_?(Xic9sv9S0B-=m&m2YT1Lo)Fi3S0j0RR;Qq8tJO*8#a{KyMD< zo&eZ?08C@RlNFY6VD17qxdT4T0Qr9ak{^KgDzJV9kiY;GG_=mYz~LtQi;>z9E3^E8y+{sNM&x7lHB9CzwI~AuxRo zbRPqNu028FPosGNaRVqSDn@Jql$4ZbcL1QHWw`+CUIP{Tz{JD^prJ<8h`{m5JAT@! zfjv>z1uDfWY?rTqju&9y1DKnj5R%>A-kSXaex3uEIJmX7wM0Zj7x%#R^>yzt;JXG8 zmailvBtW51z|0sgwuPwe_!+k(+G|$e@{%C?2fCytpymqXlmL@cfU+ZSbWB*g4V<3g zr}QI{NOpF1Utiy_urNT)3fR~JYy*JAFVoZ0b8~aJ;kD`M>6rSSfTJDY>87l#j2lu8 zs2KrinSikaP}dB2KmcLLd+rie`m{h0e~-IG&m3=jS>)IlYP>lCfvCZXvQk<;xqtJp0<`Am`xVe9r>k1q9(ui<*B{r1eEYZ@Uhrg5j%F#3 zy$L78eEs&Bv;ZB>n?E0=8R_2`NG+j96thVa#iA0C>8`eStr$49dRBk@+gsoA(2HEX zvgYB{4~foeF z&*id~mS#BY@eFa{@ikvM=w;lyyl*M)eeoS|WjmYlJa29p`6M2zZ+s6eWGU~~KE346 z{$?6rvbE7Cw}=px>2P%TCRu<|ZQPJYj>s|Du^;8g4uhnV2J6+i^TRnej(o#`HwbzO{)nG7yt+ zXDKU-Uf@QKTbIR)1Z6b^EG+r?pj`_|4BFAGfNe_lGup0shldi>6Pl>6OL zIpmEDt;q3~!qS%yJNXLh-%}@z(+3}iGv3gO`LC`pNW+>OojE4NZ}ZmNn@7w3Tx(+A zE?RY8HL{63UPribPFP)i7jxG4ioLCTCg#5kb)>xOSEx7j%00VvqU}AsGSu@Doe*zy zU)6qhP)>Z&xnaFzXcm6f9u_QlboZ(tr#R?`RG%R0`86XG69x!>y`%mp(f{p%{<%n?0bcyX{%TP{3&h=B5jZ@itA7Y#+H6f3yg%u_ zX$4j9+xqSQ8iR#(i@hzr$5-jH0Y!h1i+cL}yQD<^sdT(}j3)sTwQe7w>PaWfWrzZu z?51zfbr?oykc>5{@XtKC8(4Nkzxg(ifw%+t11#i`pp9ElP_RAT>htvrH#(9aKZ*%) zZg&3;F3~L?`j}#7rI*108Quoi=FixfFTBx+`1XBTLNUd?&5b{N?>zaM2LOeLH?ta%JJC3^+g3efP zog^*}%a7I{?im~z?kwJZ(jHXU(Z5`AZ;tAfA6V<`R2M z^bwW>V9iy;+4((ppYQm%#B)ivyW1}%(C)g|)EHLq6-O?2lP9dxZvUrK2%I?8j0y`n ztkbw?Gw3phC#5Oj{C6QP#w7RqrK@cpw(EMF36=S?ol!^=>G$Nl&8%tf=^CBJU0E}#BA+f?%E^+>qmRic zSnZ8$ch{){6rO`3egk%qf6s-IY#w*N8k-f(U@3^kho-s2M{PcZA5a$%PgkghHeL0Rt~2J_^#$?73!zj)^}V+Tto8T9)m*GZ!YN{Z zV~j45$(o9JZBgZTR^vC;AtdRGjFCHhjA|Wgt5I-~z1Lp~dtt<|y||OtiG-0K|B`zsRgKatWiM#(Z!rJzHzLNV?mFwL5*|)oR|!9NAbLF%_sLDy zq&kZ0IN6eYv9MFheU#1m!Hk~FlLZxP?$3tz#t16+n2S+ikMltw2KK6YcM_)TZWYCD zYfBr(c2A8SBQ=wsHErf{b6IxWV~C4}Qz$9v>T-BpK03*VCM-BX9uX=#h$EyDMR*dE zsiSmkq*Bb`Z8*(KiLw36LWDymnhCRDXP6yNE)F@+`Yph^yx6qkb6Isc`@K$3M0_)@bGJed*OpX-H9e+V#?V zg_%6ML4Sy54gyQ3?R3O$=QXCd-J!uUo9z$HeS+x1-Op#R+0~mC^ch_zqhSyNve}Wu zW4bewg_EtJYhI#|*4D}zaE50a{`y;au|JhwQNV4aOtpygcP(QUPW7hCjxoAd41Gup5fgMHY%P zB_H6q(tq@C*$A}qw!?Q2^T)HJh$grZ27Q$77$GrX$wo1o1UD_e& zv9q^weS-&(Us)qI&A3!@xUPP&IuOlzB`;}L1$r@hAK{$-QjxctsZ#Q)xjNm1Z0Y}I zyK=c0oGXT<7KUDNb%V}ms_F=`yU5v8tAm6o>4HI?M=bS@=)8JXOV5W}#e;}_il*uk zac|jt4${1DJ&g+H>*v3iHXTQ8sPOD&9{m=~NNfCD`0tT3oxMBwX7iWG77eddn=~9k z50*@gHqXG`J`h6u+YB!iuli}ttB3Lnc4X|vs`rT*&mn#jhEYw)1|s9F-@mjt#Wez4 z7Y)@I$rmzGQG<*a&SYCN3ay?E|1&&X;HV{MD1E3>hnQ&KcJWY7KcC?%50c}~H!s31 zW&Fq3-G2btU*sc}bpYKe`JFyfwN=Vih|xh&U9L0fG5#G9lsNn6GjzGo#}he839@EY z{%|tjiKv4_igFqD8&Igh-x+dvFx294_P$uzObCDGfhuI|lB&r6+cn#S(^Z;wkq+5W zL}k9naB`8fq4?|6Vd3@J$5+dzBtl;|%aeLoERRXp{1?Ep;NXy{wPt^2i9DRC^mjRi z8L<5-B2;3}VdA2z5t;V>I^iTJ2RZw&vm?$4BzlIup;p^XX!VOYn={r)E|VKUVz{Vk zOI!feb0IIXTcPmYAJ{p98x0p5jJpbT3@sb~3O!Dw9`vg9!l90E4vm$b zJx)Ft*oNd^LyKot43j&Znks&l=A3M!xagZ{YLA(FQ*fYEzGss3n_t zLSI*Z%jW2ut-m8rW%mMMmOn#xQpMtWh@vev#^Axa3OC==PSgEUV%}k5dEL)16yd>7 zX!2VTs&!QQ2b45ph2!Xic`gdY3WujYU-QdAWEWtrp?^G*)rR8vE^d)I<4Jgj;?y+D zNGFeK{^qaPa?xwJg5Y)ww{~>WSENI51jwGd5NXXN^(l)K}8*F*z+E%Xh9SF8mo)U z=$sIfX^J;ziVJ0W@+QvFEP{oRx`W`;qJVoM2P0;nbufE)d;8XjyJv(*q$i2I9VQMZgHfXV7^_DGoHT)Q8E0V(zqE9oWK#rMo5jBrT}} ztq~+lgVWxkq!QH(SDpk;H?x+77`xk-aQ*baQ=YL<>W&0{!j{&kXh@X^ZsxELd=1a z7Cht^F4hP5#lQ<5T~1Qtk;1tQ3m(oc2*yp+y}Ip+BbLMw!h}oqGMiXwipnxep@SEN zhUS4zAGFg8+3jtew$h~SGW;7qEGK(xSDN3(q6VwII0JCYUDSbU+@UV zxIOmt1IZ+9hPbzV%jZID^;;Y1IK+AEbT%|pj4iTrbu8u#V3nFPC{4$Gyj{2`k;*`! zcTR&3mLg!P8vKdgEi+Tii0oIHXsQVKi$V0Y;+4N%agpb)^o1J_s9N$xOoz8!A|zR( zdp)|=#~9bw-i{32HhX79Ddt#vb`oj~#78KDC%vd~&XjSsmbzvQ*%kB#9c#okddMC; z^OmkzAAwPQq?p%{2_#s5k_&9Xe9wr2YeOVY)-4tJ_O%e3hnwh(ws7KZbl9~zpUSDb z>txJkos7i|{xXNSmJ7j{TD>7sA?ldz%r#uso~@&%o)BFsI|Md_>Yw>MKcMwD9n^vd ztpzm=;dO`=>8zGidx)0BaPeaikAw5O=&LLV!J{60gFD=iDRJ)Tm6hH|>*b`^xFQp3 ztM4kmAMAV_z~ZG5jc`UcOfdfKJI`5*PN@^g4+U?KVVN}!6_x*@c*7U)IT8)817(Vz zO;Vy~VSoUC?fKNP3zvDpeZ`Egsm=QuNb55i=d_G_Ib{p_NP%Dy;oVmHwuustoo`}W zdi;6EO+@pv=;Y1lT`u|s-~1m$r>nCq(VycV6hD{A4X{Q?dj8QTwqqX|N)t)DTc4`&!O9mmd;! zpNn^qQ@(K&8iL|>{>SGK)k<)|q*yX%RsNtxvXy&H8=C7@pARDLj+1K)ENp_< z$zQlqCN@&f1|Fx`554Y@mS~wa+JneNo&bC~j)~(t__-$6t~i_fAlET}H)yhH>SKFM zt?wK*?+Xofp z?{zB-P4w1~U=me_QIF?wWMbjhvx_Jofdd5m9gXCIUoCmc4{l)vA8*fhA@kO`MGD)Q z*9uKUChszff|D}ZDq}UUt5^q$!8W+^e;sG?xO7f_7^@jkHNzVjxTtiJu~|Wr9U@57 z#WV?K_sI4u2t_&qhDl+7ql!?<4M{|~RZyJya!cfM`xU7|-A~=r+pLzdhFRBTD|?h ze3_IKqE|is#jEli&J@SDmv~O03`skecC|~(EqC$n9QHf3=gZuw@b?>7Et|fE%vtj< z94<3_5F=-vB}w&VTy<>e@lj0344}o{k{KGS33d9{>ev0uDuF`h&}KUz;?R66e8^7t zMLs_4&SIq&qCo&Nq4kQ{fR~o9NZ#a1UxiFk_AFhX{_9M{RGQ}yA}yeD^FU-Y9bHe* z@e*~_9ZtU*SahIwAA{sl=wD9diFk7sh&btAa*J6<6RsoG9NI4VKIa?Ezf+PQp?wW` zBiHtazr8k#Vi2n!MjF}R|HsqG*^t)|Yd^0-hO_C;-XMFWjl9Fs`{SmNIz7f&_Bp=n zn;v}G8m|^6YpAQZx6`*0HN_7-BXiv!LY-I4dyS+*Ngt?LVtp>Y^U~X*cuMOUx`Ts? zM97_^t7Yj>%DSqZ&gZHwZIx)vzYQ*l@A$x`y(hu8crq^veyNjoe~IBzX4WX&tmE8z z!L8R^SnF$VTniNxa^FEskV?4mp^@5f-5s+6cV<~l^b%x^x7Vr;#UlnXbqA41>!o%e zN?(ASmd+>siisqVK&g0b%Z2$tv3k)wSD{7ZsCWqL;uaUt9Qb|LMF{mZgFipEG0B@K zD2Ly+dr1r(!qX>S7*WH?xb{azND4=>_Zw9eCe+vVWs`Wowz&JF+FUmSF0B@|SJc{H zuu@rq&L9a^pKi-zfYAU&>}#6Em!DY8yBvc>q=PJnESylfp+$(z#npS)&_&YGyK^*G#`1BqzSN$q#IGz zz~^w1l%JOTqiG{Hb5zAP1-LpdKN`N;cIQJHt2p{ev$yEGHiHzCM1qoky~(57U-ff> zx++(E#r|k!)D%h?5%COy`h=H>scW z>857@hjgM_B*_YdBv4F|fM8PZa>eM9_F6fnro=*lB2Xf4&6PIoMpQQH_$b=FU5%@z zP6jZ7`HhH)x*TI1`CO7L7PzNIVedE5Zn8i9FZPpT>I>Ale238gAV&0S7LCzWnN`QM zl*Xg^jf4F*ufdxyekXlyOyHwAcK5^Qdc6j6|4K?jvr#G^;90$GLq2F1U_2j^WNFTL3(SH@$Up}7YWHbMR(%!eZy7ZL&jBfh8(JOZMs^{_4u5u5^cjMXo zXXL}M-PAeho-`^kAi;8_>Fc})%M&4>75{$ca=GeS$rM=N_w_=U;DMdPvKgMi^u3ya zWx`XRob1mcVSnY@iXp+z zWxE$-B$RR^xlT$VQSs6Y;*2&SYS4#VK23w=oBfiQN3)#N+;t23N!vdk?Jyr|>bXi( z-i`ikp&&6=F3GCTl`AUG^L+d~hEOlV)l^coq2#wGHHS&Cszl|ddksWnHY)zqbj!w8 zWD5VrOJO4EdN>1z3Gdx=Q{?4*bqAYv+D_i=6=nkx7XTXlpV@kNzIfzQzY(M;rz%?~ HZ3g)-0^(3p literal 0 HcmV?d00001 diff --git a/build.cmd b/build.cmd deleted file mode 100644 index d878123..0000000 --- a/build.cmd +++ /dev/null @@ -1,6 +0,0 @@ -start cmd.exe @cmd /k "ocra disenchanter_up.rb --gemfile .\Gemfile --dll ruby_builtin_dlls\libssp-0.dll --dll ruby_builtin_dlls\libgmp-10.dll --dll ruby_builtin_dlls\libgcc_s_seh-1.dll --dll ruby_builtin_dlls\libwinpthread-1.dll --dll ruby_builtin_dlls\libssl-1_1-x64.dll --dll ruby_builtin_dlls\libcrypto-1_1-x64.dll --icon BE_icon.ico && exit" -timeout /T 20 /nobreak -echo. > .build.lockfile -start cmd.exe @cmd /k "ocra disenchanter.rb --gemfile .\Gemfile --dll ruby_builtin_dlls/libffi-7.dll --dll ruby_builtin_dlls\libssp-0.dll --dll ruby_builtin_dlls\libgmp-10.dll --dll ruby_builtin_dlls\libgcc_s_seh-1.dll --dll ruby_builtin_dlls\libwinpthread-1.dll --dll ruby_builtin_dlls\libssl-1_1-x64.dll --dll ruby_builtin_dlls\libcrypto-1_1-x64.dll --icon BE_icon.ico && exit" -timeout /T 20 /nobreak -del .build.lockfile diff --git a/scripts/build_main.sh b/scripts/build_main.sh index 0166dd0..6407c4f 100644 --- a/scripts/build_main.sh +++ b/scripts/build_main.sh @@ -11,7 +11,7 @@ ocra src/main.rb \ --dll ruby_builtin_dlls/libwinpthread-1.dll \ --dll ruby_builtin_dlls/libssl-1_1-x64.dll \ --dll ruby_builtin_dlls/libcrypto-1_1-x64.dll \ - --icon BE_icon.ico \ + --icon ./assets/BE_icon.ico \ --output ./build/disenchanter.exe rm ./build/.build.lockfile From f4bb9f81ea5297e24467a21b7b16fbe52cd3bda2 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Fri, 16 Feb 2024 20:10:40 +0100 Subject: [PATCH 53/63] Derived general menu, reimplemented debug menu --- src/class/menu/debug_menu.rb | 49 +++++++++++++++++++++++++++ src/class/menu/menu.rb | 54 ++++++++++++++++++++++++++++++ src/modules/debug.rb | 65 ------------------------------------ 3 files changed, 103 insertions(+), 65 deletions(-) create mode 100644 src/class/menu/debug_menu.rb create mode 100644 src/class/menu/menu.rb delete mode 100644 src/modules/debug.rb diff --git a/src/class/menu/debug_menu.rb b/src/class/menu/debug_menu.rb new file mode 100644 index 0000000..fa15333 --- /dev/null +++ b/src/class/menu/debug_menu.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require_relative 'menu' + +# An interactive debug menu +class DebugMenu < Menu + def initialize(client) + menu_text = 'What would you like to do?' + things_todo = { + '1' => 'Write player_loot to file', + '2' => 'Write recipes of lootId to file', + '3' => 'Write loot info of lootId to file', + 'm' => 'Toggle debug mode', + 'x' => 'Back to main menu' + } + answer_display = 'Option' + + super(client, menu_text, answer_display, things_todo) + end + + # Handles the debug step the user selected + # @param thing_todo Option name + # @return true if done + def handle_option(thing_todo) + case thing_todo + when '1' + player_loot = @client.req_get_player_loot + File.write('disenchanter_loot.json', player_loot.to_json) + puts('Okay, written to disenchanter_loot.json.') + when '2' + loot_id = ask("Which lootId would you like the recipes for?\n".light_cyan) + recipes = @client.req_get_recipes_for_item(loot_id) + File.write('disenchanter_recipes.json', recipes.to_json) + puts('Okay, written to disenchanter_recipes.json.') + when '3' + loot_id = ask("Which lootId would you like the info for?\n".light_cyan) + loot_info = @client.req_get_loot_info(loot_id) + File.write('disenchanter_lootinfo.json', loot_info.to_json) + puts('Okay, written to disenchanter_lootinfo.json.') + when 'm' + @client.debug = !@client.debug + puts @client.debug ? 'Debug mode enabled' : 'Debug mode disabled' + when 'x' + return true + end + + false + end +end diff --git a/src/class/menu/menu.rb b/src/class/menu/menu.rb new file mode 100644 index 0000000..41c7043 --- /dev/null +++ b/src/class/menu/menu.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# An interactive terminal menu +class Menu + # @param client Client connector + # @param menu_text Text presented at the menu's top + # @param answer_display Display text for answers + # @param things_todo Dict of selectable options + def initialize(client, menu_text, answer_display, things_todo) + @client = client + @menu_text = menu_text + @answer_display = answer_display + @things_todo = things_todo + + @things_done = [] + end + + def run_loop + done = false + + until done + thing_todo = user_input_check( + "\n#{@menu_text}\n\n".light_cyan + todo_str, + @things_todo.keys, + @answer_display, + 'default' + ) + @things_done << thing_todo + puts separator + "\n\nOption chosen: #{@things_todo[thing_todo]}".light_white + + done = handle_option(thing_todo) + puts separator + end + end + + def todo_str + todo_string = '' + @things_todo.each do |k, v| + todo_string += "[#{k}] ".light_white + todo_string += if @things_done.include? k + "#{v} (done)\n".light_green + else + "#{v}\n".light_cyan + end + end + + todo_string + end + + # Override this! + def handle_option(todo) + puts "Stump for option: #{todo}." + end +end diff --git a/src/modules/debug.rb b/src/modules/debug.rb deleted file mode 100644 index 88d547f..0000000 --- a/src/modules/debug.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -# Handles debug related stuff -# @param client Client connector -def handle_debug(client) - done = false - things_todo = { - '1' => 'Write player_loot to file', - '2' => 'Write recipes of lootId to file', - '3' => 'Write loot info of lootId to file', - 'm' => 'Enable debug mode', - 'x' => 'Back to main menu' - } - things_done = [] - - until done - todo_string = '' - things_todo.each do |k, v| - todo_string += "[#{k}] ".light_white - todo_string += if things_done.include? k - "#{v} (done)\n".light_green - else - "#{v}\n".light_cyan - end - end - - todo = - user_input_check( - "\nWhat would you like to do?\n\n".light_cyan + todo_string + - 'Option: '.white, - things_todo.keys, - '', - '' - ) - things_done << todo - - puts separator - puts - - puts "Option chosen: #{things_todo[todo]}".light_white - - case todo - when '1' - player_loot = client.req_get_player_loot - File.write('disenchanter_loot.json', player_loot.to_json) - puts('Okay, written to disenchanter_loot.json.') - when '2' - loot_id = ask("Which lootId would you like the recipes for?\n".light_cyan) - recipes = client.req_get_recipes_for_item(loot_id) - File.write('disenchanter_recipes.json', recipes.to_json) - puts('Okay, written to disenchanter_recipes.json.') - when '3' - loot_id = ask("Which lootId would you like the info for?\n".light_cyan) - loot_info = client.req_get_loot_info(loot_id) - File.write('disenchanter_lootinfo.json', loot_info.to_json) - puts('Okay, written to disenchanter_lootinfo.json.') - when 'm' - client.debug = true - puts 'Debug mode enabled.' - when 'x' - done = true - end - puts separator - end -end From fd242d1846c985ce9bd8fea5cff7be90e75f2729 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Fri, 16 Feb 2024 20:11:28 +0100 Subject: [PATCH 54/63] Refactoring + simplification --- src/modules/handlers/champions/tokens.rb | 8 +- src/modules/handlers/materials.rb | 95 +++++++++++-------- .../handlers/materials/mastery_tokens.rb | 14 ++- .../handlers/materials/mythic_essence.rb | 4 +- src/modules/stat_submission.rb | 4 +- src/modules/update/checker.rb | 79 ++++++++------- src/modules/user_input.rb | 2 +- 7 files changed, 114 insertions(+), 92 deletions(-) diff --git a/src/modules/handlers/champions/tokens.rb b/src/modules/handlers/champions/tokens.rb index ebb6307..fd3887b 100644 --- a/src/modules/handlers/champions/tokens.rb +++ b/src/modules/handlers/champions/tokens.rb @@ -21,6 +21,12 @@ def handle_champions_tokens(player_loot, loot_shards) token_champion_count = token6_champion_ids.length + token7_champion_ids.length puts "Found #{token_champion_count} champions with owned mastery tokens".light_black + adjust_token_counts(loot_shards, token6_champion_ids, token7_champion_ids) +rescue StandardError => e + handle_exception(e, 'Champion Shards by Tokens') +end + +def adjust_token_counts(loot_shards, token6_champion_ids, token7_champion_ids) loot_shards.each do |l| if token6_champion_ids.include? l['storeItemId'] l['count'] -= 2 @@ -30,6 +36,4 @@ def handle_champions_tokens(player_loot, loot_shards) l['count_keep'] += 1 end end -rescue StandardError => e - handle_exception(e, 'Champion Shards by Tokens') end diff --git a/src/modules/handlers/materials.rb b/src/modules/handlers/materials.rb index c138e79..2ae902a 100644 --- a/src/modules/handlers/materials.rb +++ b/src/modules/handlers/materials.rb @@ -9,54 +9,67 @@ # @param client Client connector def handle_materials(client) done = false - things_todo = { + things_done = [] + + until done + todo = user_input_check( + "\nWhat would you like to do?\n\n".light_cyan + + materials_todo_str(things_done) + ' Option: '.white, + materials_todo.keys, + '', + '' + ) + + things_done << todo + puts separator + "\n\nOption chosen: #{materials_todo[todo]}".light_white + done = handle_material_option(todo, client) + puts separator + end +end + +# Returns a hash with all possible Options and their menu item names +def materials_todo + { '1' => 'Mythic Essence', '2' => 'Key Fragments', '3' => 'Capsules', '4' => 'Mastery Tokens', 'x' => 'Back to main menu' } - things_done = [] +end - until done - todo_string = '' - things_todo.each do |k, v| - todo_string += "[#{k}] ".light_white - todo_string += if things_done.include? k - "#{v} (done)\n".light_green - else - "#{v}\n".light_cyan - end - end - - todo = - user_input_check( - "\nWhat would you like to do?\n\n".light_cyan + - "#{todo_string} Option: ", - things_todo.keys, - '', - '' - ) - things_done << todo +# Outputs things to do as a pretty string, done things are marked accordingly +# @param things_done Options already processed +def materials_todo_str(things_done) + todo_string = '' + materials_todo.each do |k, v| + todo_string += "[#{k}] ".light_white + todo_string += if things_done.include? k + "#{v} (done)\n".light_green + else + "#{v}\n".light_cyan + end + end - puts separator - puts - - puts "Option chosen: #{things_todo[todo]}".light_white - - case todo - when '1' - handle_mythic_essence(client) - when '2' - handle_key_fragments(client) - when '3' - handle_capsules(client) - when '4' - handle_mastery_tokens(client) - when 'x' - done = true - end + todo_string +end - puts separator +# Calls the corresponding material method +# @param thing_todo Option name +# @return true if done +def handle_material_option(thing_todo, client) + case thing_todo + when '1' + handle_mythic_essence(client) + when '2' + handle_key_fragments(client) + when '3' + handle_capsules(client) + when '4' + handle_mastery_tokens(client) + when 'x' + return true end -end + + false +end \ No newline at end of file diff --git a/src/modules/handlers/materials/mastery_tokens.rb b/src/modules/handlers/materials/mastery_tokens.rb index 16543ce..fa7ea53 100644 --- a/src/modules/handlers/materials/mastery_tokens.rb +++ b/src/modules/handlers/materials/mastery_tokens.rb @@ -12,15 +12,13 @@ def handle_mastery_tokens(client) recipes6 = client.req_get_recipes_for_item('CHAMPION_TOKEN_6-1') recipes7 = client.req_get_recipes_for_item('CHAMPION_TOKEN_7-1') - recipe6_cost = - recipes6.select do |r| - r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' - end + recipe6_cost = recipes6.select do |r| + r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' + end recipe6_cost = recipe6_cost[0]['slots'][1]['quantity'] - recipe7_cost = - recipes7.select do |r| - r['recipeName'] == 'CHAMPION_TOKEN_7_redeem_withessence' - end + recipe7_cost = recipes7.select do |r| + r['recipeName'] == 'CHAMPION_TOKEN_7_redeem_withessence' + end recipe7_cost = recipe7_cost[0]['slots'][1]['quantity'] loot_overall_tokens = diff --git a/src/modules/handlers/materials/mythic_essence.rb b/src/modules/handlers/materials/mythic_essence.rb index c565a41..4649869 100644 --- a/src/modules/handlers/materials/mythic_essence.rb +++ b/src/modules/handlers/materials/mythic_essence.rb @@ -67,9 +67,9 @@ def handle_mythic_essence(client) "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]}?", (1..loot_essence['count'].to_i) .to_a - .map!(&:to_s) .append('all') - .append('x'), + .append('x') + .map!(&:to_s), "[1..#{loot_essence['count']}|all|x]" ) diff --git a/src/modules/stat_submission.rb b/src/modules/stat_submission.rb index 6fa607d..dd28a09 100644 --- a/src/modules/stat_submission.rb +++ b/src/modules/stat_submission.rb @@ -41,7 +41,7 @@ def gather_stats(stat_tracker) out = "Your stats:\n".light_blue stats = ['Actions', 'Disenchanted', 'Opened', 'Crafted', 'Redeemed', 'Blue Essence', 'Orange Essence'] - out += stats.map { |stat| wrap_stat_line(stat, stat_tracker.send(stat.downcase.gsub(' ', '_'))) }.join + out + stats.map { |stat| wrap_stat_line(stat, stat_tracker.send(stat.downcase.gsub(' ', '_'))) }.join end def wrap_stat_line(name, value) @@ -49,5 +49,5 @@ def wrap_stat_line(name, value) numlen = 7 out = pad(name, strlen) out += pad(value.to_s, numlen, right: false).light_white - out += "\n" + "#{out}\n" end diff --git a/src/modules/update/checker.rb b/src/modules/update/checker.rb index ef5721e..c513df8 100644 --- a/src/modules/update/checker.rb +++ b/src/modules/update/checker.rb @@ -1,45 +1,52 @@ # frozen_string_literal: true def check_update(version_local) - uri = - URI( - 'https://api.github.com/repos/marvinscham/disenchanter/releases/latest' - ) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - req = Net::HTTP::Get.new(uri, 'Content-Type': 'application/json') - res = http.request req - ans = JSON.parse(res.body) - - version_local = - Gem::Version.new(version_local.delete_prefix('v').delete_suffix('-beta')) - version_remote = - Gem::Version.new( - ans['tag_name'].delete_prefix('v').delete_suffix('-beta') - ) - - if version_remote > version_local - puts "New version #{ans['tag_name']} available!".light_yellow - if ans_y.include? user_input_check( - 'Would you like to download the new version now?', - ans_yn, - ans_yn_d - ) - `curl https://github.com/marvinscham/disenchanter/releases/download/#{ans['tag_name']}/disenchanter_up.exe -L -o disenchanter_up.exe` - puts 'Done downloading!'.green - - pid = spawn('start cmd.exe @cmd /k "disenchanter_up.exe"') - Process.detach(pid) - puts 'Exiting...'.light_black - exit - end - elsif version_local > version_remote + tag_name = grab_remote_tag_name + + version_local = Gem::Version.new(version_local.delete_prefix('v').delete_suffix('-beta')) + version_remote = Gem::Version.new(tag_name.delete_prefix('v').delete_suffix('-beta')) + + if version_remote == version_local + puts "You're up to date!".green + return + end + + if version_local > version_remote puts 'Welcome to the future!'.light_magenta puts "Latest remote version: v#{version_remote}".light_blue - else - puts "You're up to date!".green + return end + + puts "New version #{tag_name} available!".light_yellow + download_remote_version(tag_name) rescue StandardError => e handle_exception(e, 'self update') end + +def grab_remote_tag_name + uri = URI('https://api.github.com/repos/marvinscham/disenchanter/releases/latest') + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + req = Net::HTTP::Get.new(uri, 'Content-Type': 'application/json') + res = http.request req + JSON.parse(res.body)['tag_name'] +end + +# Downloads the specified version from GitHub +# @param version Disenchanter version to download +def download_remote_version(version) + if ans_y.include? user_input_check( + 'Would you like to download the new version now?', + ans_yn, + ans_yn_d + ) + `curl https://github.com/marvinscham/disenchanter/releases/download/#{version}/disenchanter_up.exe -L -o disenchanter_up.exe` + puts 'Done downloading!'.green + + pid = spawn('start cmd.exe @cmd /k "disenchanter_up.exe"') + Process.detach(pid) + puts 'Exiting...'.light_black + exit + end +end \ No newline at end of file diff --git a/src/modules/user_input.rb b/src/modules/user_input.rb index 25154a9..39b325f 100644 --- a/src/modules/user_input.rb +++ b/src/modules/user_input.rb @@ -15,7 +15,7 @@ def user_input_check(question, answers, answer_display, color_preset = 'default' "CONFIRM: #{question} ".light_magenta + answer_display.to_s.light_white + ': '.light_magenta when 'default' question = - "#{question} ".light_cyan + answer_display.to_s.light_white + ': '.light_cyan + question.light_cyan + "#{answer_display}: ".light_white end until answers.include? input From c3c55beeb542bd7ff044a1ac53bb5d68eb954896 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Fri, 16 Feb 2024 21:16:42 +0100 Subject: [PATCH 55/63] Added summoner info debug option --- src/class/menu/debug_menu.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/class/menu/debug_menu.rb b/src/class/menu/debug_menu.rb index fa15333..8313b17 100644 --- a/src/class/menu/debug_menu.rb +++ b/src/class/menu/debug_menu.rb @@ -10,6 +10,7 @@ def initialize(client) '1' => 'Write player_loot to file', '2' => 'Write recipes of lootId to file', '3' => 'Write loot info of lootId to file', + '4' => 'Write summoner info to file', 'm' => 'Toggle debug mode', 'x' => 'Back to main menu' } @@ -37,6 +38,9 @@ def handle_option(thing_todo) loot_info = @client.req_get_loot_info(loot_id) File.write('disenchanter_lootinfo.json', loot_info.to_json) puts('Okay, written to disenchanter_lootinfo.json.') + when '4' + File.write('disenchanter_summoner.json', @client.req_get_current_summoner.to_json) + puts('Okay, written to disenchanter_summoner.json.') when 'm' @client.debug = !@client.debug puts @client.debug ? 'Debug mode enabled' : 'Debug mode disabled' From e2828e92d5f19a3f25f32fde709aed326cb813ff Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sat, 17 Feb 2024 05:05:17 +0100 Subject: [PATCH 56/63] Full refactoring --- src/class/client.rb | 10 +- src/class/dictionary.rb | 19 +++ src/class/menu/champions_menu.rb | 54 ++++++ src/class/menu/debug_menu.rb | 22 +-- src/class/menu/main_menu.rb | 74 ++++++++ src/class/menu/materials_menu.rb | 45 +++++ src/class/menu/menu.rb | 17 +- src/class/menu/mythic_menu.rb | 54 ++++++ src/main.rb | 118 +++---------- src/modules/common_strings.rb | 2 + src/modules/detect_client.rb | 105 +++++++----- src/modules/handlers.rb | 13 -- .../handlers/{materials => }/capsules.rb | 2 + src/modules/handlers/champions.rb | 154 +++++++---------- src/modules/handlers/champions/exceptions.rb | 41 ----- src/modules/handlers/champions/owned.rb | 16 -- .../collection.rb => champions_collection.rb} | 0 src/modules/handlers/champions_exclusions.rb | 43 +++++ .../mastery.rb => champions_mastery.rb} | 26 +-- src/modules/handlers/champions_owned.rb | 30 ++++ .../tokens.rb => champions_tokens.rb} | 5 +- src/modules/handlers/debug.rb | 34 ++++ src/modules/handlers/emotes.rb | 57 ++++--- src/modules/handlers/eternals.rb | 2 + src/modules/handlers/exception.rb | 6 +- src/modules/handlers/generic.rb | 106 ------------ src/modules/handlers/generic_loot.rb | 135 +++++++++++++++ src/modules/handlers/icons.rb | 2 + .../handlers/{materials => }/key_fragments.rb | 0 src/modules/handlers/mastery_tokens.rb | 160 ++++++++++++++++++ src/modules/handlers/materials.rb | 72 +------- .../handlers/materials/mastery_tokens.rb | 134 --------------- .../handlers/materials/mythic_essence.rb | 118 ------------- src/modules/handlers/mythic_essence.rb | 94 ++++++++++ src/modules/handlers/skins.rb | 2 + src/modules/handlers/tacticians.rb | 2 + src/modules/handlers/wards.rb | 2 + src/modules/open_url.rb | 2 + src/modules/update/checker.rb | 5 +- src/modules/user_input.rb | 8 +- 40 files changed, 1004 insertions(+), 787 deletions(-) create mode 100644 src/class/dictionary.rb create mode 100644 src/class/menu/champions_menu.rb create mode 100644 src/class/menu/main_menu.rb create mode 100644 src/class/menu/materials_menu.rb create mode 100644 src/class/menu/mythic_menu.rb delete mode 100644 src/modules/handlers.rb rename src/modules/handlers/{materials => }/capsules.rb (98%) delete mode 100644 src/modules/handlers/champions/exceptions.rb delete mode 100644 src/modules/handlers/champions/owned.rb rename src/modules/handlers/{champions/collection.rb => champions_collection.rb} (100%) create mode 100644 src/modules/handlers/champions_exclusions.rb rename src/modules/handlers/{champions/mastery.rb => champions_mastery.rb} (67%) create mode 100644 src/modules/handlers/champions_owned.rb rename src/modules/handlers/{champions/tokens.rb => champions_tokens.rb} (90%) create mode 100644 src/modules/handlers/debug.rb delete mode 100644 src/modules/handlers/generic.rb create mode 100644 src/modules/handlers/generic_loot.rb rename src/modules/handlers/{materials => }/key_fragments.rb (100%) create mode 100644 src/modules/handlers/mastery_tokens.rb delete mode 100644 src/modules/handlers/materials/mastery_tokens.rb delete mode 100644 src/modules/handlers/materials/mythic_essence.rb create mode 100644 src/modules/handlers/mythic_essence.rb diff --git a/src/class/client.rb b/src/class/client.rb index 96ddee6..b284163 100644 --- a/src/class/client.rb +++ b/src/class/client.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true +require 'base64' +require 'net/http' +require 'json' + require_relative '../modules/detect_client' # Holds port and token info class Client - attr_accessor :stat_tracker, :debug + attr_accessor :stat_tracker, :debug, :dry_run # @param stat_tracker StatTracker def initialize(stat_tracker) @@ -16,6 +20,7 @@ def initialize(stat_tracker) end @stat_tracker = stat_tracker @debug = false + @dry_run = false end def host @@ -52,6 +57,9 @@ def request_get(path) def request_post(path, _body) puts "Posting against #{host}/#{path}".light_black if @debug + puts 'DRY RUN ENABLED - actually did nothing'.light_red if @dry_run + return if @dry_run + # create_client do |http| # uri = URI("#{host}/#{path}") # req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') diff --git a/src/class/dictionary.rb b/src/class/dictionary.rb new file mode 100644 index 0000000..d375b89 --- /dev/null +++ b/src/class/dictionary.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Holds constants for Loot IDs +class Dictionary + BLUE_ESSENCE = 'CURRENCY_champion' + ORANGE_ESSENCE = 'CURRENCY_cosmetic' + MYTHIC_ESSENCE = 'CURRENCY_mythic' + + CHAMPION_SHARD = 'CHAMPION_RENTAL' + CHAMPION_PERMANENT = 'CHAMPION' + + MASTERY_6_TOKEN = 'CHAMPION_TOKEN_6' + MASTERY_7_TOKEN = 'CHAMPION_TOKEN_7' + + MASTERY_6_RECIPE = 'CHAMPION_TOKEN_6_redeem_withessence' + MASTERY_7_RECIPE = 'CHAMPION_TOKEN_7_redeem_withessence' + + RANDOM_SKIN_SHARD = 'CHEST_291' +end diff --git a/src/class/menu/champions_menu.rb b/src/class/menu/champions_menu.rb new file mode 100644 index 0000000..b881281 --- /dev/null +++ b/src/class/menu/champions_menu.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require_relative 'menu' + +require_relative '../../modules/handlers/champions_collection' +require_relative '../../modules/handlers/champions_exclusions' +require_relative '../../modules/handlers/champions_mastery' +require_relative '../../modules/handlers/champions_owned' +require_relative '../../modules/handlers/champions_tokens' + +# Menu to select and call filtering options +class ChampionsMenu < Menu + attr_reader :loot_shards + + def initialize(client, loot_shards) + menu_text = 'Okay, which option would you like to go by?' + things_todo = { + '1' => 'Disenchant all champion shards', + '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', + '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', + '4' => 'Keep enough (1/2) shards to fully master all champions ' \ + '(only disenchant shards that have no possible use)', + '5' => 'Keep one shard of each champion regardless of mastery', + 'x' => 'Back to main menu' + } + answer_display = 'Option' + + super(client, menu_text, answer_display, things_todo) + @loot_shards = loot_shards + end + + # Calls the corresponding champion shard handling method + # @param thing_todo Option name + # @return true if done + def handle_option(thing_todo) + case thing_todo + when '1', 'x' + # no filtering needed -> done + when '2' + @loot_shards = handle_champions_tokens(@client, @loot_shards) + when '3' + @loot_shards = handle_champions_mastery(@client, @loot_shards) + when '4' + @loot_shards = handle_champions_mastery(@client, @loot_shards, keep_all: true) + when '5' + @loot_shards = handle_champions_collection(@loot_shards) + end + + @loot_shards = @loot_shards.select { |l| l['count'].positive? } + @loot_shards = @loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } + + true + end +end diff --git a/src/class/menu/debug_menu.rb b/src/class/menu/debug_menu.rb index 8313b17..60075ca 100644 --- a/src/class/menu/debug_menu.rb +++ b/src/class/menu/debug_menu.rb @@ -11,6 +11,7 @@ def initialize(client) '2' => 'Write recipes of lootId to file', '3' => 'Write loot info of lootId to file', '4' => 'Write summoner info to file', + 'd' => 'Toggle dry run', 'm' => 'Toggle debug mode', 'x' => 'Back to main menu' } @@ -25,22 +26,17 @@ def initialize(client) def handle_option(thing_todo) case thing_todo when '1' - player_loot = @client.req_get_player_loot - File.write('disenchanter_loot.json', player_loot.to_json) - puts('Okay, written to disenchanter_loot.json.') + debug_save_player_loot(@client) when '2' - loot_id = ask("Which lootId would you like the recipes for?\n".light_cyan) - recipes = @client.req_get_recipes_for_item(loot_id) - File.write('disenchanter_recipes.json', recipes.to_json) - puts('Okay, written to disenchanter_recipes.json.') + debug_save_recipe(@client) when '3' - loot_id = ask("Which lootId would you like the info for?\n".light_cyan) - loot_info = @client.req_get_loot_info(loot_id) - File.write('disenchanter_lootinfo.json', loot_info.to_json) - puts('Okay, written to disenchanter_lootinfo.json.') + debug_save_loot_info(@client) when '4' - File.write('disenchanter_summoner.json', @client.req_get_current_summoner.to_json) - puts('Okay, written to disenchanter_summoner.json.') + debug_save_summoner_info(@client) + when 'd' + @client.dry_run = !@client.dry_run + @client.debug = @client.dry_run + puts @client.dry_run ? 'Dry run + debug enabled' : 'Dry run + debug disabled' when 'm' @client.debug = !@client.debug puts @client.debug ? 'Debug mode enabled' : 'Debug mode disabled' diff --git a/src/class/menu/main_menu.rb b/src/class/menu/main_menu.rb new file mode 100644 index 0000000..baac901 --- /dev/null +++ b/src/class/menu/main_menu.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require_relative 'menu' + +require_relative '../../modules/handlers/champions' +require_relative '../../modules/handlers/emotes' +require_relative '../../modules/handlers/eternals' +require_relative '../../modules/handlers/exception' +require_relative '../../modules/handlers/icons' +require_relative '../../modules/handlers/skins' +require_relative '../../modules/handlers/tacticians' +require_relative '../../modules/handlers/wards' +require_relative '../../modules/handlers/debug' +require_relative '../../modules/handlers/materials' + +require_relative '../../modules/open_url' +require_relative '../../modules/stat_submission' + +# The main menu +class MainMenu < Menu + def initialize(client) + menu_text = 'What would you like to do? (Hint: go top to bottom so you don\'t miss anything!)' + things_todo = { + '1' => 'Materials', + '2' => 'Champions', + '3' => 'Skins', + '4' => 'Tacticians', + '5' => 'Eternals', + '6' => 'Emotes', + '7' => 'Ward Skins', + '8' => 'Icons', + 's' => 'Open Disenchanter Global Stats', + 'r' => 'Open GitHub repository', + 'd' => 'Debug Tools', + 'x' => 'Exit' + } + answer_display = 'Option' + + super(client, menu_text, answer_display, things_todo) + end + + def handle_option(todo) + case todo + when '1' + handle_materials(@client) + when '2' + handle_champions(@client) + when '3' + handle_skins(@client) + when '4' + handle_tacticians(@client) + when '5' + handle_eternals(@client) + when '6' + handle_emotes(@client) + when '7' + handle_ward_skins(@client) + when '8' + handle_icons(@client) + when 's' + open_stats + when 'r' + open_github + when 'd' + handle_debug(@client) + when 'x' + return true + end + + @client.refresh_loot + puts separator + false + end +end diff --git a/src/class/menu/materials_menu.rb b/src/class/menu/materials_menu.rb new file mode 100644 index 0000000..cb69dd1 --- /dev/null +++ b/src/class/menu/materials_menu.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative 'menu' + +require_relative '../../modules/handlers/mythic_essence' +require_relative '../../modules/handlers/key_fragments' +require_relative '../../modules/handlers/capsules' +require_relative '../../modules/handlers/mastery_tokens' + +# Menu for handling materials such as fragments, mythic essence or capsules +class MaterialsMenu < Menu + def initialize(client) + menu_text = 'What would you like to do?' + things_todo = { + '1' => 'Mythic Essence', + '2' => 'Key Fragments', + '3' => 'Capsules', + '4' => 'Mastery Tokens', + 'x' => 'Back to main menu' + } + answer_display = 'Option' + + super(client, menu_text, answer_display, things_todo) + end + + # Calls the corresponding material handling method + # @param thing_todo Option name + # @return true if done + def handle_option(thing_todo) + case thing_todo + when '1' + handle_mythic_essence(@client) + when '2' + handle_key_fragments(@client) + when '3' + handle_capsules(@client) + when '4' + handle_mastery_tokens(@client) + when 'x' + return true + end + + false + end +end diff --git a/src/class/menu/menu.rb b/src/class/menu/menu.rb index 41c7043..e387c94 100644 --- a/src/class/menu/menu.rb +++ b/src/class/menu/menu.rb @@ -2,6 +2,8 @@ # An interactive terminal menu class Menu + attr_reader :things_todo + # @param client Client connector # @param menu_text Text presented at the menu's top # @param answer_display Display text for answers @@ -15,22 +17,29 @@ def initialize(client, menu_text, answer_display, things_todo) @things_done = [] end + # Runs a menu loop until it's either done or the user bails to the main menu + # @return true if user bailed def run_loop done = false + bail = false - until done + until done || bail thing_todo = user_input_check( "\n#{@menu_text}\n\n".light_cyan + todo_str, @things_todo.keys, @answer_display, - 'default' + @client.dry_run ? 'dry' : 'default' ) @things_done << thing_todo puts separator + "\n\nOption chosen: #{@things_todo[thing_todo]}".light_white done = handle_option(thing_todo) + bail = thing_todo == 'x' + puts separator end + + bail end def todo_str @@ -48,7 +57,7 @@ def todo_str end # Override this! - def handle_option(todo) - puts "Stump for option: #{todo}." + def handle_option(thing_todo) + puts "Stump for option: #{thing_todo}." end end diff --git a/src/class/menu/mythic_menu.rb b/src/class/menu/mythic_menu.rb new file mode 100644 index 0000000..40c1ee1 --- /dev/null +++ b/src/class/menu/mythic_menu.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require_relative 'menu' + +require_relative '../dictionary' + +# Menu to handle what to do with mythic essence +class MythicMenu < Menu + attr_reader :thing_todo, :recipe + + def initialize(client) + menu_text = 'Okay, what would you like to craft?' + things_todo = { + '1' => 'Blue Essence', + '2' => 'Orange Essence', + '3' => 'Random Skin Shards', + 'x' => 'Back to main menu' + } + answer_display = 'Option' + + super(client, menu_text, answer_display, things_todo) + @thing_todo = '' + @recipe = '' + end + + def handle_option(thing_todo) + @thing_todo = thing_todo + + case thing_todo + when '1' + thing_to_craft = Dictionary::BLUE_ESSENCE + when '2' + thing_to_craft = Dictionary::ORANGE_ESSENCE + when '3' + thing_to_craft = Dictionary::RANDOM_SKIN_SHARD + when 'x' + return true + end + + recipes = @client.req_get_recipes_for_item(Dictionary::MYTHIC_ESSENCE) + recipes = recipes.select { |r| r['outputs'][0]['lootName'] == thing_to_craft } + + if recipes.empty? + puts "Recipes for #{@things_todo[@thing_todo]} seem to be unavailable.".yellow + return + end + @recipe = recipes[0] + + puts "Recipe found: #{@recipe['contextMenuText']} for " \ + "#{@recipe['slots'][0]['quantity']} Mythic Essence".light_blue + + true + end +end diff --git a/src/main.rb b/src/main.rb index 0619894..8eb3eff 100644 --- a/src/main.rb +++ b/src/main.rb @@ -1,38 +1,30 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require 'net/https' -require 'base64' -require 'json' -require 'colorize' -require 'launchy' -require 'open-uri' -require 'rbconfig' - require_relative 'class/client' +require_relative 'class/menu/main_menu' require_relative 'class/stat_tracker' require_relative 'modules/common_strings' -require_relative 'modules/debug' -require_relative 'modules/handlers' -require_relative 'modules/loot_metainfo' -require_relative 'modules/open_url' -require_relative 'modules/stat_submission' require_relative 'modules/user_input' require_relative 'modules/update/checker' def run - if File.exist?('./build/.build.lockfile') - puts 'Detected build environment, skipping execution...'.light_yellow - sleep 2 - exit - end + check_build_env current_version = 'v1.6.0' stat_tracker = StatTracker.new client = Client.new(stat_tracker) + greet(client, current_version) + + MainMenu.new(client).run_loop + + finish(stat_tracker) +end + +def greet(client, current_version) puts 'Hi! :)'.light_green puts "Running Disenchanter #{current_version}".light_blue check_update(current_version) @@ -40,93 +32,25 @@ def run puts '[CTRL + C]'.light_white + '.'.light_blue puts separator + check_summoner(client) +end + +def check_summoner(client) summoner = client.req_get_current_summoner if summoner['gameName'].nil? || summoner['gameName'].empty? puts 'Could not grab summoner info. Try restarting your League Client.'.light_red ask exit_string exit 1 end + puts "\nYou're logged in as #{summoner['gameName']} ##{summoner['tagLine']}.".light_blue puts separator puts "\nYour loot is safe, no actions will be taken until you confirm a banner like this:".light_blue puts 'CONFIRM: Perform this action? [y|n]'.light_magenta puts separator +end - done = false - things_todo = { - '1' => 'Materials', - '2' => 'Champions', - '3' => 'Skins', - '4' => 'Tacticians', - '5' => 'Eternals', - '6' => 'Emotes', - '7' => 'Ward Skins', - '8' => 'Icons', - 's' => 'Open Disenchanter Global Stats', - 'r' => 'Open GitHub repository', - 'd' => 'Debug Tools', - 'x' => 'Exit' - } - things_done = [] - - until done - todo_string = '' - things_todo.each do |k, v| - todo_string += "[#{k}] ".light_white - todo_string += if things_done.include? k - "#{v} (done)\n".light_green - else - "#{v}\n".light_cyan - end - end - - todo = - user_input_check( - "\nWhat would you like to do? (Hint: go top to bottom so you don't miss anything!)\n\n".light_cyan + - "#{todo_string}Option: ", - things_todo.keys, - '', - '' - ) - things_done << todo - - puts separator - puts - - puts "Option chosen: #{things_todo[todo]}".light_white - - case todo - when '1' - handle_materials(client) - when '2' - handle_champions(client) - when '3' - handle_skins(client) - when '4' - handle_tacticians(client) - when '5' - handle_eternals(client) - when '6' - handle_emotes(client) - when '7' - handle_ward_skins(client) - when '8' - handle_icons(client) - when 's' - open_stats - when 'r' - open_github - when 'd' - handle_debug(client) - when 'x' - done = true - else - puts 'Invalid state, exiting.'.yellow - end - client.refresh_loot - puts separator - end - +def finish(stat_tracker) puts "That's it!".light_green if stat_tracker.actions.positive? puts "We saved you about #{stat_tracker.actions * 3} seconds of waiting for animations to finish.".light_green @@ -137,4 +61,12 @@ def run ask exit_string end +def check_build_env + return unless File.exist?('./build/.build.lockfile') + + puts 'Detected build environment, skipping execution...'.light_yellow + sleep 1 + exit +end + run diff --git a/src/modules/common_strings.rb b/src/modules/common_strings.rb index 1b00250..07f7ffa 100644 --- a/src/modules/common_strings.rb +++ b/src/modules/common_strings.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'colorize' + def exit_string 'Press Enter to exit.'.cyan end diff --git a/src/modules/detect_client.rb b/src/modules/detect_client.rb index 104c9f8..3dcdffa 100644 --- a/src/modules/detect_client.rb +++ b/src/modules/detect_client.rb @@ -4,59 +4,86 @@ require 'win32/registry' if RbConfig::CONFIG['host_os'] =~ win_ident require 'win32/shortcut' if RbConfig::CONFIG['host_os'] =~ win_ident +# rubocop:disable Style/MixinUsage include Win32 if RbConfig::CONFIG['host_os'] =~ win_ident +# rubocop:enable Style/MixinUsage +# rubocop:disable Style/ClassAndModuleChildren if RbConfig::CONFIG['host_os'] =~ win_ident module Win32::Registry::Constants KEY_WOW64_64KEY = 0x0100 KEY_WOW64_32KEY = 0x0200 end end +# rubocop:enable Style/ClassAndModuleChildren def grab_lockfile - is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) - if is_windows.zero? - lockfile = 'lockfile' - begin - keyname = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live' - reg = Win32::Registry::HKEY_CURRENT_USER.open(keyname, - Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY) - lockfile = "#{reg['InstallLocation']}/lockfile" - puts 'Found client via registry'.light_black - rescue StandardError - # do nothing - end - - if lockfile == 'lockfile' - begin - sc = Shortcut.open( - 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Riot Games\\League of Legends.lnk' - ) - scpath = sc.path.split('\\') - path = scpath[0..-3].join('\\') - lockfile = "#{path}\\League of Legends\\lockfile" - puts 'Found client via start menu'.light_black - rescue StandardError - # just keep going - end - end - end + is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/).zero? - begin - contents = File.read("C:\\Riot Games\\League of Legends\\#{lockfile}") - puts 'Found client at standard path'.light_black - rescue StandardError - begin - contents = File.read(lockfile) - rescue StandardError - puts 'Failed to automatically find your League Client.'.light_red - puts 'Make sure your client is running and logged into your account.'.light_red - puts 'If it\'s running and you\'re seeing this, please place the script directly in your League Client folder.'.light_red - end + if is_windows + credentials = try_grab_lockfile_registry + credentials = try_grab_lockfile_start_menu if credentials == '' end - _leagueclient, _unk_port, port, password = contents.split(':') + credentials = try_grab_lockfile_default_path if credentials == '' + credentials = try_grab_lockfile_locally if credentials == '' + + _leagueclient, _unk_port, port, password = credentials.split(':') token = Base64.encode64("riot:#{password.chomp}") [port, token] end + +def try_grab_lockfile_registry + keyname = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Riot Game league_of_legends.live' + reg = Win32::Registry::HKEY_CURRENT_USER.open( + keyname, + Win32::Registry::KEY_READ | Win32::Registry::KEY_WOW64_32KEY + ) + + credentials = File.read("#{reg['InstallLocation']}/lockfile") + puts 'Found client via registry'.light_black + + credentials +rescue StandardError + # Just keep going + '' +end + +def try_grab_lockfile_start_menu + sc = Shortcut.open( + 'C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Riot Games\\League of Legends.lnk' + ) + scpath = sc.path.split('\\') + path = scpath[0..-3].join('\\') + + credentials = File.read("#{path}\\League of Legends\\lockfile") + puts 'Found client via start menu'.light_black + + credentials +rescue StandardError + # Just keep going + '' +end + +def try_grab_lockfile_default_path + credentials = File.read("C:\\Riot Games\\League of Legends\\#{lockfile}") + puts 'Found client at standard path'.light_black + + credentials +rescue StandardError + # Just keep going + '' +end + +def try_grab_lockfile_locally + credentials = File.read(lockfile) + puts 'Found client locally'.light_black + + credentials +rescue StandardError + puts 'Failed to automatically find your League Client.'.light_red + puts 'Make sure your client is running and logged into your account.'.light_red + puts 'If it\'s running and you\'re seeing this, ' \ + 'please place the script directly in your League Client folder.'.light_red +end diff --git a/src/modules/handlers.rb b/src/modules/handlers.rb deleted file mode 100644 index 194b5e1..0000000 --- a/src/modules/handlers.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -require_relative 'handlers/generic' - -require_relative 'handlers/champions' -require_relative 'handlers/emotes' -require_relative 'handlers/eternals' -require_relative 'handlers/exception' -require_relative 'handlers/icons' -require_relative 'handlers/materials' -require_relative 'handlers/skins' -require_relative 'handlers/tacticians' -require_relative 'handlers/wards' diff --git a/src/modules/handlers/materials/capsules.rb b/src/modules/handlers/capsules.rb similarity index 98% rename from src/modules/handlers/materials/capsules.rb rename to src/modules/handlers/capsules.rb index 3e03108..3cbe651 100644 --- a/src/modules/handlers/materials/capsules.rb +++ b/src/modules/handlers/capsules.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative '../loot_metainfo' + # Opens keyless chests/capsules # @param client Client connector def handle_capsules(client) diff --git a/src/modules/handlers/champions.rb b/src/modules/handlers/champions.rb index f0a377a..d56f736 100644 --- a/src/modules/handlers/champions.rb +++ b/src/modules/handlers/champions.rb @@ -1,28 +1,19 @@ # frozen_string_literal: true -require_relative 'champions/collection' -require_relative 'champions/exceptions' -require_relative 'champions/mastery' -require_relative 'champions/owned' -require_relative 'champions/tokens' +require_relative '../../class/menu/champions_menu' + +require_relative '../loot_metainfo' + +require_relative 'champions_collection' +require_relative 'champions_exclusions' +require_relative 'champions_mastery' +require_relative 'champions_owned' +require_relative 'champions_tokens' # Handles any champion shard and champion permanent related loot actions # @param client Client connector def handle_champions(client) - player_loot = client.req_get_player_loot - loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } - - loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } - if count_loot_items(loot_perms).positive? && (ans_y.include? user_input_check( - 'Should we include champion permanents in this process?', - ans_yn, - ans_yn_d - )) - loot_shards = - player_loot.select do |l| - l['type'] == 'CHAMPION_RENTAL' || l['type'] == 'CHAMPION' - end - end + loot_shards = init_champion_shard_selection(client) if count_loot_items(loot_shards).zero? puts 'Found no champion shards to disenchant.'.yellow @@ -30,77 +21,35 @@ def handle_champions(client) end puts "Found #{count_loot_items(loot_shards)} champion shards.".light_blue + loot_shards = handle_champions_owned(loot_shards) - loot_shards.each do |s| - s['count_keep'] = 0 - s['disenchant_note'] = '' - end - loot_shards_not_owned = - loot_shards.reject { |s| s['redeemableStatus'] == 'ALREADY_OWNED' } - - if loot_shards_not_owned.empty? - puts "Found no shards of champions you don't own yet.".light_blue - elsif ans_y.include? user_input_check( - "Keep a shard for champions you don't own yet?", - ans_yn, - ans_yn_d - ) - loot_shards = handle_champions_owned(loot_shards) - end + champions_menu = ChampionsMenu.new(client, loot_shards) + bail = champions_menu.run_loop + return if bail - disenchant_modes = { - '1' => 'Disenchant all champion shards', - '2' => 'Keep enough (1/2) shards for champions you own mastery 6/7 tokens for', - '3' => 'Keep enough (1/2) shards to fully master champions at least at mastery level x (select from 1 to 6)', - '4' => 'Keep enough (1/2) shards to fully master all champions (only disenchant shards that have no possible use)', - '5' => 'Keep one shard of each champion regardless of mastery', - 'x' => 'Cancel' - } - - modes_string = '' - disenchant_modes.each do |k, v| - modes_string += "[#{k}] ".light_white - modes_string += "#{v}\n".light_cyan - end + loot_shards = champions_menu.loot_shards - disenchant_shards_mode = - user_input_check( - "Okay, which option would you like to go by?\n#{modes_string}" \ - 'Option: ', - disenchant_modes.keys, - '[1|2|3|4|5|x]', - '' - ) - if disenchant_shards_mode == 'x' - puts 'Champion shard disenchanting canceled.'.yellow + if count_loot_items(loot_shards).zero? + puts "Job's already done: no champion shards left matching your selection.".green return end - case disenchant_shards_mode - when '1' - # no filtering needed -> done - when '2' - loot_shards = handle_champions_tokens(player_loot, loot_shards) - when '3' - loot_shards = handle_champions_mastery(client, loot_shards) - when '4' - loot_shards = handle_champions_mastery(client, loot_shards, keep_all: true) - when '5' - loot_shards = handle_champions_collection(loot_shards) - else - puts 'Invalid state, exiting.'.yellow - return - end + present_champion_selection(loot_shards) - loot_shards = loot_shards.select { |l| l['count'].positive? } - loot_shards = - loot_shards.sort_by { |l| [l['disenchant_note'], l['itemDesc']] } + pre_exclusion_count = count_loot_items(loot_shards) + loot_shards = handle_champions_exclusions(loot_shards) - if count_loot_items(loot_shards).zero? - puts "Job's already done: no champion shards left matching your selection.".green - return - end + return if count_loot_items(loot_shards).zero? + present_champion_selection(loot_shards) if pre_exclusion_count != count_loot_items(loot_shards) + + execute_champions_disenchant(client, loot_shards) + puts 'Done!'.green +rescue StandardError => e + handle_exception(e, 'Champion Shards') +end + +def present_champion_selection(loot_shards) puts "We'd disenchant #{count_loot_items(loot_shards)} champion shards using the option you chose:".light_blue loot_shards.each do |l| loot_value = l['disenchantValue'] * l['count'] @@ -108,27 +57,41 @@ def handle_champions(client) print pad(l['itemDesc'], 15).light_white print ' @ '.light_black print pad("#{loot_value} BE", 8, right: false).light_black - if l['count_keep'].positive? - puts " keeping #{l['count_keep']}".green - elsif l['disenchant_note'].length.positive? - puts " #{l['disenchant_note']}" - else - puts - end + print_champion_disenchant_addendum(l) + puts end +end - loot_shards = handle_champions_exceptions(loot_shards) +def print_champion_disenchant_addendum(shard) + if shard['count_keep'].positive? + print " keeping #{shard['count_keep']}".green + elsif shard['disenchant_note'].length.positive? + print " #{shard['disenchant_note']}" + end +end + +def init_champion_shard_selection(client) + player_loot = client.req_get_player_loot + loot_shards = player_loot.select { |l| l['type'] == Dictionary::CHAMPION_SHARD } + loot_perms = player_loot.select { |l| l['type'] == Dictionary::CHAMPION_PERMANENT } + if count_loot_items(loot_perms).positive? && (ans_y.include? user_input_check( + 'Should we include champion permanents in this process?', + ans_yn, + ans_yn_d + )) + loot_shards.concat(loot_perms) + end + + loot_shards +end + +def execute_champions_disenchant(client, loot_shards) total_be_value = 0 loot_shards.each do |l| total_be_value += l['disenchantValue'] * l['count'] end - if count_loot_items(loot_shards).zero? - puts 'All remaining champions have been excluded, skipping...'.yellow - return - end - if ans_y.include? user_input_check( "Disenchant #{count_loot_items(loot_shards)} champion shards for #{total_be_value} Blue Essence?", ans_yn, @@ -148,8 +111,5 @@ def handle_champions(client) end end threads.each(&:join) - puts 'Done!'.green end -rescue StandardError => e - handle_exception(e, 'Champion Shards') end diff --git a/src/modules/handlers/champions/exceptions.rb b/src/modules/handlers/champions/exceptions.rb deleted file mode 100644 index 41e77e8..0000000 --- a/src/modules/handlers/champions/exceptions.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -# Allows adding exceptions to a previously made Champion Shard disenchantment selection -# @param loot_shards Loot array, champion shards and permanents only -def handle_champions_exceptions(loot_shards) - exclusions_str = '' - exclusions_done = false - exclusions_done_more = '' - exclusions_arr = [] - - until exclusions_done - if ans_y.include? user_input_check( - "Would you like to add #{exclusions_done_more}exclusions?", - ans_yn, - ans_yn_d - ) - exclusions_str += ',' - exclusions_str += ask( - 'Okay, which champions? '.light_cyan + - '(case-sensitive, comma-separated)'.light_white + - ': '.light_cyan - ) - - exclusions_done_more = 'more ' - - exclusions_arr = exclusions_str.split(/\s*,\s*/) - exclusions_matched = - loot_shards.select { |l| exclusions_arr.include? l['itemDesc'] } - - print 'Exclusions recognized: '.green - exclusions_matched.each { |e| print "#{e['itemDesc'].light_white} " } - puts - else - exclusions_done = true - end - end - - loot_shards.reject { |l| exclusions_arr.include? l['itemDesc'] } -rescue StandardError => e - handle_exception(e, 'Champion Shard Exceptions') -end diff --git a/src/modules/handlers/champions/owned.rb b/src/modules/handlers/champions/owned.rb deleted file mode 100644 index 24f9df8..0000000 --- a/src/modules/handlers/champions/owned.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -# Keeps a shard for each champion not owned yet -# @param loot_shards Loot array, pre-filtered to only champion shards and permanents -def handle_champions_owned(loot_shards) - loot_shards.each do |l| - unless l['redeemableStatus'] == 'ALREADY_OWNED' - l['count'] -= 1 - l['count_keep'] += 1 - end - end - - loot_shards -rescue StandardError => e - handle_exception(e, 'Owned Champion Shards') -end diff --git a/src/modules/handlers/champions/collection.rb b/src/modules/handlers/champions_collection.rb similarity index 100% rename from src/modules/handlers/champions/collection.rb rename to src/modules/handlers/champions_collection.rb diff --git a/src/modules/handlers/champions_exclusions.rb b/src/modules/handlers/champions_exclusions.rb new file mode 100644 index 0000000..2c2780b --- /dev/null +++ b/src/modules/handlers/champions_exclusions.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +# Allows adding exceptions to a previously made Champion Shard disenchantment selection +# @param loot_shards Loot array, champion shards and permanents only +def handle_champions_exclusions(loot_shards) + exclusions_str = '' + exclusions_arr = [] + exclusions_done = false + + until exclusions_done + if ans_y.include? user_input_check( + 'Would you like to add exclusions?', + ans_yn, + ans_yn_d + ) + exclusions_arr += handle_champion_exclusion(loot_shards, exclusions_str) + else + exclusions_done = true + end + end + + loot_shards.reject { |l| exclusions_arr.include? l['itemDesc'] } +rescue StandardError => e + handle_exception(e, 'Champion Shard Exceptions') +end + +def handle_champion_exclusion(loot_shards, exclusions_str) + exclusions_str += ',' + exclusions_str += ask( + 'Okay, which champions? '.light_cyan + + '(case-sensitive, comma-separated)'.light_white + + ': '.light_cyan + ) + + exclusions_arr = exclusions_str.split(/\s*,\s*/) + exclusions_matched = loot_shards.select { |l| exclusions_arr.include? l['itemDesc'] } + + print 'Exclusions recognized: '.green + exclusions_matched.each { |e| print "#{e['itemDesc'].light_white} " } + puts + + exclusions_arr +end diff --git a/src/modules/handlers/champions/mastery.rb b/src/modules/handlers/champions_mastery.rb similarity index 67% rename from src/modules/handlers/champions/mastery.rb rename to src/modules/handlers/champions_mastery.rb index 7cde8dc..3d0a788 100644 --- a/src/modules/handlers/champions/mastery.rb +++ b/src/modules/handlers/champions_mastery.rb @@ -32,20 +32,24 @@ def handle_champions_mastery(client, loot_shards, keep_all: false) end loot_shards.each do |l| - if mastery7_champion_ids.include? l['storeItemId'] - l['disenchant_note'] = 'at mastery 7'.light_black - elsif mastery6_champion_ids.include? l['storeItemId'] - l['count'] -= 1 - l['count_keep'] += 1 - elsif keep_all || (threshold_champion_ids.include? l['storeItemId']) - l['count'] -= 2 - l['count_keep'] += 2 - else - l['disenchant_note'] = 'below threshold'.yellow - end + adjust_shard_counts_by_threshold(l, keep_all, mastery6_champion_ids, mastery7_champion_ids, threshold_champion_ids) end loot_shards rescue StandardError => e handle_exception(e, 'Champion Shards by Mastery') end + +def adjust_shard_counts_by_threshold(shard, keep_all, m6_ids, m7_ids, threshold_ids) + if m7_ids.include? shard['storeItemId'] + shard['disenchant_note'] = 'at mastery 7'.light_black + elsif m6_ids.include? shard['storeItemId'] + shard['count'] -= 1 + shard['count_keep'] += 1 + elsif keep_all || (threshold_ids.include? shard['storeItemId']) + shard['count'] -= 2 + shard['count_keep'] += 2 + else + shard['disenchant_note'] = 'below threshold'.yellow + end +end diff --git a/src/modules/handlers/champions_owned.rb b/src/modules/handlers/champions_owned.rb new file mode 100644 index 0000000..23be24a --- /dev/null +++ b/src/modules/handlers/champions_owned.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Keeps a shard for each champion not owned yet +# @param loot_shards Loot array, pre-filtered to only champion shards and permanents +def handle_champions_owned(loot_shards) + loot_shards.each do |s| + s['count_keep'] = 0 + s['disenchant_note'] = '' + end + loot_shards_not_owned = loot_shards.reject { |s| s['redeemableStatus'] == 'ALREADY_OWNED' } + + if loot_shards_not_owned.empty? + puts "Found no shards of champions you don't own yet.".light_blue + elsif ans_y.include? user_input_check( + "Keep a shard for champions you don't own yet?", + ans_yn, + ans_yn_d + ) + loot_shards.each do |l| + unless l['redeemableStatus'] == 'ALREADY_OWNED' + l['count'] -= 1 + l['count_keep'] += 1 + end + end + end + + loot_shards +rescue StandardError => e + handle_exception(e, 'Owned Champion Shards') +end diff --git a/src/modules/handlers/champions/tokens.rb b/src/modules/handlers/champions_tokens.rb similarity index 90% rename from src/modules/handlers/champions/tokens.rb rename to src/modules/handlers/champions_tokens.rb index fd3887b..66016ed 100644 --- a/src/modules/handlers/champions/tokens.rb +++ b/src/modules/handlers/champions_tokens.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true # Keeps only shards to max out champions with owned mastery 6/7 tokens -# @param player_loot Loot array to grab mastery tokens +# @param client Client connector # @param loot_shards Loot array pre-filtered to only champion shards and permanents -def handle_champions_tokens(player_loot, loot_shards) +def handle_champions_tokens(client, loot_shards) token6_champion_ids = [] token7_champion_ids = [] + player_loot = client.req_get_player_loot loot_mastery_tokens = player_loot.select { |l| l['type'] == 'CHAMPION_TOKEN' } diff --git a/src/modules/handlers/debug.rb b/src/modules/handlers/debug.rb new file mode 100644 index 0000000..42225b7 --- /dev/null +++ b/src/modules/handlers/debug.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative '../../class/menu/debug_menu' + +# Wrapper for debug +# @param client Client connector +def handle_debug(client) + DebugMenu.new(client).run_loop +end + +def debug_save_player_loot(client) + player_loot = client.req_get_player_loot + File.write('disenchanter_loot.json', player_loot.to_json) + puts('Okay, written to disenchanter_loot.json.') +end + +def debug_save_recipe(client) + loot_id = ask("Which lootId would you like the recipes for?\n".light_cyan) + recipes = client.req_get_recipes_for_item(loot_id) + File.write('disenchanter_recipes.json', recipes.to_json) + puts('Okay, written to disenchanter_recipes.json.') +end + +def debug_save_loot_info(client) + loot_id = ask("Which lootId would you like the info for?\n".light_cyan) + loot_info = client.req_get_loot_info(loot_id) + File.write('disenchanter_lootinfo.json', loot_info.to_json) + puts('Okay, written to disenchanter_lootinfo.json.') +end + +def debug_save_summoner_info(client) + File.write('disenchanter_summoner.json', client.req_get_current_summoner.to_json) + puts('Okay, written to disenchanter_summoner.json.') +end diff --git a/src/modules/handlers/emotes.rb b/src/modules/handlers/emotes.rb index f8cbfac..9890c06 100644 --- a/src/modules/handlers/emotes.rb +++ b/src/modules/handlers/emotes.rb @@ -1,43 +1,56 @@ # frozen_string_literal: true -# Wrapper for emotes, also handles non-disenchantable esports emotes +require_relative 'generic_loot' + +# Wrapper for emotes # @param client Client connector # @note No shards for emotes def handle_emotes(client) handle_generic(client, 'Emotes', 'EMOTE') + client.refresh_loot + # Re-handle icons in case new disenchant candidates came up + handle_generic(client, 'Emotes', 'EMOTE') if handle_esports_emotes(client) +end - player_loot = client.req_get_player_loot - esports_emotes = player_loot.select do |l| - l['type'] == 'EMOTE' \ - && l['disenchantLootName'] == '' \ - && l['redeemableStatus'] == 'ALREADY_OWNED' - end +# Rerolls non-disenchantable esports emotes into disenchantable ones +# @return true if re-run is needed +def handle_esports_emotes(client) + esports_emotes = find_esports_emotes(client) if count_loot_items(esports_emotes).zero? puts 'Found no Esports Emotes to re-roll.'.yellow - return + return false end puts "Found #{count_loot_items(esports_emotes)} Esports Emotes." - if ans_y.include? user_input_check( + + unless ans_y.include? user_input_check( "Re-roll #{count_loot_items(esports_emotes)} already owned Esports Emotes?", ans_yn, ans_yn_d, 'confirm' ) - client.stat_tracker.add_crafted(count_loot_items(esports_emotes)) - threads = - esports_emotes.map do |g| - Thread.new { client.req_post_recipe('EMOTE_forge', g['lootId'], g['count']) } - end - threads.each(&:join) - - puts 'Done!'.green - client.refresh_loot - else - return + return false end - # Re-handle icons in case new disenchant candidates came up - handle_generic(client, 'Emotes', 'EMOTE') + client.stat_tracker.add_crafted(count_loot_items(esports_emotes)) + threads = + esports_emotes.map do |g| + Thread.new { client.req_post_recipe('EMOTE_forge', g['lootId'], g['count']) } + end + threads.each(&:join) + + puts 'Done!'.green + client.refresh_loot + + true +end + +def find_esports_emotes(client) + player_loot = client.req_get_player_loot + player_loot.select do |l| + l['type'] == 'EMOTE' \ + && l['disenchantLootName'] == '' \ + && l['redeemableStatus'] == 'ALREADY_OWNED' + end end diff --git a/src/modules/handlers/eternals.rb b/src/modules/handlers/eternals.rb index 07075a5..8c8cd12 100644 --- a/src/modules/handlers/eternals.rb +++ b/src/modules/handlers/eternals.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative 'generic_loot' + # Wrapper for eternals sets and their shards # @param client Client connector def handle_eternals(client) diff --git a/src/modules/handlers/exception.rb b/src/modules/handlers/exception.rb index 4ce372f..c43f49f 100644 --- a/src/modules/handlers/exception.rb +++ b/src/modules/handlers/exception.rb @@ -2,8 +2,10 @@ def handle_exception(exception, name) puts "An error occurred while handling #{name}.".light_red - puts 'Please take a screenshot and create an issue at https://github.com/marvinscham/disenchanter/issues/new'.light_red + puts 'Please take a screenshot and create an issue at ' \ + 'https://github.com/marvinscham/disenchanter/issues/new'.light_red puts "If you don't have a GitHub account, send it to dev@marvinscham.de".light_red - puts "Exception Occurred #{exception.class}. Message: #{exception.message}. Backtrace: \n #{exception.backtrace.join("\n")}" + puts "Exception Occurred #{exception.class}. Message: #{exception.message}. " \ + "Backtrace: \n #{exception.backtrace.join("\n")}" puts 'Skipping this step...'.yellow end diff --git a/src/modules/handlers/generic.rb b/src/modules/handlers/generic.rb deleted file mode 100644 index 752adef..0000000 --- a/src/modules/handlers/generic.rb +++ /dev/null @@ -1,106 +0,0 @@ -# frozen_string_literal: true - -# Handles generic loot types -# @param client Client connector -# @param name Display name ("Skin Shards") -# @param type Riot loot type name ("SKIN_RENTAL") -def handle_generic(client, name, type) - player_loot = client.req_get_player_loot - disenchant_all = true - - loot_generic = player_loot.select { |l| l['type'] == type } - # Things like esports icons cannot be disenchanted -> drop - loot_generic = loot_generic.reject { |l| l['disenchantLootName'] == '' } - if count_loot_items(loot_generic).zero? - puts "Found no #{name} to disenchant.".yellow - return - end - - puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue - - contains_unowned_items = false - loot_generic.each do |l| - contains_unowned_items = true if l['redeemableStatus'] != 'ALREADY_OWNED' - end - - if contains_unowned_items - user_option = - user_input_check( - "Keep #{name} you don't own yet?\n".light_cyan + - '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + - "No\n".light_cyan + '[x] '.light_white + - "Exit to main menu\n".light_cyan + 'Option: '.white, - %w[y n x], - '[y|n|x]', - '' - ) - - case user_option - when 'x' - puts 'Action cancelled'.yellow - return - when 'y' - disenchant_all = false - loot_generic = loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } - puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue - end - end - - if count_loot_items(loot_generic).zero? - puts "Found no owned #{name} to disenchant.".yellow - return - end - - total_oe_value = 0 - total_be_value = 0 - loot_generic.each do |g| - total_be_value += g['disenchantValue'] * g['count'] if g['disenchantLootName'] == 'CURRENCY_champion' - total_oe_value += g['disenchantValue'] * g['count'] if g['disenchantLootName'] == 'CURRENCY_cosmetic' - end - - loot_name_index = 'itemDesc' - loot_name_index = 'localizedName' if loot_generic[0]['itemDesc'] == '' - - loot_generic = - loot_generic.sort_by do |l| - [l['redeemableStatus'], l[loot_name_index]] - end - - puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue - loot_generic.each do |l| - loot_value = l['disenchantValue'] * l['count'] - loot_currency = l['disenchantLootName'] == 'CURRENCY_champion' ? 'BE' : 'OE' - - print pad("#{l['count']}x ", 5, right: false).light_black - print pad(l[loot_name_index], 30).light_white - print ' @ '.light_black - print pad("#{loot_value} #{loot_currency}", 8, right: false).light_black - print ' (not owned)'.yellow if disenchant_all && l['redeemableStatus'] != 'ALREADY_OWNED' - puts - end - - disenchant_info = '' - disenchant_info += "#{total_oe_value} Orange Essence" if total_oe_value.positive? - disenchant_info += ' and ' if total_oe_value.positive? && total_be_value.positive? - disenchant_info += "#{total_be_value} Blue Essence" if total_be_value.positive? - - if ans_y.include? user_input_check( - "Disenchant #{count_loot_items(loot_generic)} #{name} for #{disenchant_info}?", - ans_yn, - ans_yn_d, - 'confirm' - ) - client.stat_tracker.add_disenchanted(count_loot_items(loot_generic)) - client.stat_tracker.add_blue_essence(total_be_value) - client.stat_tracker.add_orange_essence(total_oe_value) - threads = - loot_generic.map do |g| - Thread.new { client.req_post_recipe(g['disenchantRecipeName'], g['lootId'], g['count']) } - end - threads.each(&:join) - - puts 'Done!'.green - end -rescue StandardError => e - handle_exception(e, name) -end diff --git a/src/modules/handlers/generic_loot.rb b/src/modules/handlers/generic_loot.rb new file mode 100644 index 0000000..26cc318 --- /dev/null +++ b/src/modules/handlers/generic_loot.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +require_relative '../loot_metainfo' +require_relative '../../class/dictionary' + +# Handles generic loot types +# @param client Client connector +# @param name Display name ("Skin Shards") +# @param type Riot loot type name ("SKIN_RENTAL") +def handle_generic(client, name, type) + loot_generic = select_generic_loot(client, type) + if count_loot_items(loot_generic).zero? + puts "Found no #{name} to disenchant.".yellow + return + end + + puts "Found #{count_loot_items(loot_generic)} #{name}.".light_blue + + loot_generic = handle_generic_owned(loot_generic, name) + return if loot_generic == false + + if count_loot_items(loot_generic).zero? + puts "Found no owned #{name} to disenchant.".yellow + return + end + + loot_name_index = loot_generic[0]['itemDesc'] == '' ? 'localizedName' : 'itemDesc' + totals = prepare_generic_totals(loot_generic) + disenchant_info = create_generic_disenchant_info(loot_generic, loot_name_index, name, totals) + + unless ans_y.include? user_input_check( + "Disenchant #{count_loot_items(loot_generic)} #{name} for #{disenchant_info}?", + ans_yn, + ans_yn_d, + 'confirm' + ) + return + end + + execute_generic_disenchant(client, loot_generic, totals) + + puts 'Done!'.green +rescue StandardError => e + handle_exception(e, name) +end + +def select_generic_loot(client, type) + player_loot = client.req_get_player_loot + generic_loot = player_loot.select { |l| l['type'] == type } + # Things like esports icons cannot be disenchanted -> drop + generic_loot.reject { |l| l['disenchantLootName'] == '' } +end + +def handle_generic_owned(loot_generic, name) + contains_unowned_items = false + loot_generic.each do |l| + contains_unowned_items = true if l['redeemableStatus'] != 'ALREADY_OWNED' + end + + if contains_unowned_items + user_option = + user_input_check( + "Keep #{name} you don't own yet?\n".light_cyan + + '[y] '.light_white + "Yes\n".light_cyan + '[n] '.light_white + + "No\n".light_cyan + '[x] '.light_white + + "Exit to main menu\n".light_cyan + 'Option: '.white, + %w[y n x], + '[y|n|x]', + '' + ) + + case user_option + when 'x' + puts 'Action cancelled'.yellow + return false + when 'y' + loot_generic = loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } + puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue + end + end + + loot_generic +end + +def prepare_generic_totals(loot_generic) + totals = { + 'oe' => 0, + 'be' => 0 + } + loot_generic.each do |g| + totals['be'] += g['disenchantValue'] * g['count'] if g['disenchantLootName'] == Dictionary::BLUE_ESSENCE + totals['oe'] += g['disenchantValue'] * g['count'] if g['disenchantLootName'] == Dictionary::ORANGE_ESSENCE + end + + totals +end + +def create_generic_disenchant_info(loot_generic, loot_name_index, name, totals) + loot_generic = loot_generic.sort_by do |l| + [l['redeemableStatus'], l[loot_name_index]] + end + + puts "We'd disenchant #{count_loot_items(loot_generic)} #{name} using the option you chose:".light_blue + loot_generic.each do |l| + create_generic_info_single(l, loot_name_index) + end + + disenchant_info = '' + disenchant_info += "#{totals['oe']} Orange Essence" if totals['oe'].positive? + disenchant_info += ' and ' if totals['be'].positive? && totals['oe'].positive? + disenchant_info += "#{totals['be']} Blue Essence" if totals['be'].positive? + disenchant_info +end + +def create_generic_info_single(loot, loot_name_index) + loot_value = loot['disenchantValue'] * loot['count'] + loot_currency = loot['disenchantLootName'] == Dictionary::BLUE_ESSENCE ? 'BE' : 'OE' + + print pad("#{loot['count']}x ", 5, right: false).light_black + print pad(loot[loot_name_index], 30).light_white + print ' @ '.light_black + print pad("#{loot_value} #{loot_currency}", 8, right: false).light_black + print ' (not owned)'.yellow if loot['redeemableStatus'] != 'ALREADY_OWNED' + puts +end + +def execute_generic_disenchant(client, loot_generic, totals) + client.stat_tracker.add_disenchanted(count_loot_items(loot_generic)) + client.stat_tracker.add_blue_essence(totals['be']) + client.stat_tracker.add_orange_essence(totals['oe']) + threads = loot_generic.map do |g| + Thread.new { client.req_post_recipe(g['disenchantRecipeName'], g['lootId'], g['count']) } + end + threads.each(&:join) +end diff --git a/src/modules/handlers/icons.rb b/src/modules/handlers/icons.rb index 0460f7b..ebda058 100644 --- a/src/modules/handlers/icons.rb +++ b/src/modules/handlers/icons.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative 'generic_loot' + # Wrapper for summoner icons # @param client Client connector # @note No shards for icons! diff --git a/src/modules/handlers/materials/key_fragments.rb b/src/modules/handlers/key_fragments.rb similarity index 100% rename from src/modules/handlers/materials/key_fragments.rb rename to src/modules/handlers/key_fragments.rb diff --git a/src/modules/handlers/mastery_tokens.rb b/src/modules/handlers/mastery_tokens.rb new file mode 100644 index 0000000..fd30b14 --- /dev/null +++ b/src/modules/handlers/mastery_tokens.rb @@ -0,0 +1,160 @@ +# frozen_string_literal: true + +require_relative '../loot_metainfo' +require_relative '../../class/dictionary' + +# Redeems mastery 6/7 tokens as efficiently as possible: +# Prefers champion shard over champion permanent over blue essence +# @param client Client connector +def handle_mastery_tokens(client) + loot_mastery_tokens = grab_upgradable_tokens(client) + + if loot_mastery_tokens.count.zero? + puts 'Found no upgradable set of Mastery Tokens.'.yellow + return + end + + loot_mastery_tokens = loot_mastery_tokens.sort_by { |l| [l['lootName'], l['itemDesc']] } + puts "We could upgrade the following champions:\n".light_blue + + needed_resources = determine_token_crafting_recources(client, loot_mastery_tokens) + owned_essence = client.req_get_player_loot.select { |l| l['lootId'] == Dictionary::BLUE_ESSENCE }[0]['count'] + + essence_missing = needed_resources['essence'] - owned_essence + if essence_missing.positive? + puts "You're missing #{essence_missing} Blue Essence needed to proceed. Skipping...".yellow + return + end + + execute_token_crafting(client, loot_mastery_tokens, needed_resources) + puts 'Done!'.green +rescue StandardError => e + handle_exception(e, 'token upgrades') +end + +# Reduces player loot to a set of tokens that can be upgraded +# @param client Client connector +def grab_upgradable_tokens(client) + client.req_get_player_loot.select do |l| + (l['lootName'] == Dictionary::MASTERY_6_TOKEN && l['count'] == 2) || + (l['lootName'] == Dictionary::MASTERY_7_TOKEN && l['count'] == 3) + end +end + +# Calculates the cheapest way to upgrade each token set +# @param client Client connector +# @param loot_mastery_tokens Set of upgradable tokens +def determine_token_crafting_recources(client, loot_mastery_tokens) + player_loot = client.req_get_player_loot + needed_resources = { + 'shards' => 0, + 'perms' => 0, + 'essence' => 0 + } + + loot_mastery_tokens.each do |t| + print pad(t['itemDesc'], 15, right: false).light_white + print ' to Mastery Level '.light_black + print t['lootName'][-1].light_white + print ' using '.light_black + + calc_token_crafting_resource(player_loot, t, needed_resources) + puts + end + puts + + needed_resources +end + +def check_token_ref_crafting_material(player_loot, ref_id, type) + type_id = type == 'shard' ? Dictionary::CHAMPION_SHARD : Dictionary::CHAMPION_PERMANENT + + ref_mat = player_loot.select do |l| + l['type'] == type_id && ref_id == l['storeItemId'].to_s + end + + !ref_mat.empty? && ref_mat[0]['count'].positive? +end + +def calc_token_crafting_resource(player_loot, token, needed_resources) + if check_token_ref_crafting_material(player_loot, token['refId'], 'shard') + print 'a champion shard.'.green + needed_resources['shards'] += 1 + token['upgrade_type'] = 'shard' + elsif check_token_ref_crafting_material(player_loot, token['refId'], 'perm') + print 'a champion permanent.'.green + needed_resources['perms'] += 1 + token['upgrade_type'] = 'permanent' + else + recipe_cost = mastery_upgrade_cost(client, (token['lootName'])[-1]) + print "#{recipe_cost} Blue Essence.".yellow + needed_resources['essence'] += recipe_cost + token['upgrade_type'] = 'essence' + end +end + +# Grabs token set blue essence upgrade cost +# @param client Client connector +# @param level Token level (6/7) +def mastery_upgrade_cost(client, level) + recipes = client.req_get_recipes_for_item("#{Dictionary.const_get("MASTERY_#{level}_TOKEN")}-1") + + recipe_cost = recipes.select do |r| + r['recipeName'] == "CHAMPION_TOKEN_#{level}_redeem_withessence" + end + recipe_cost[0]['slots'][1]['quantity'] +end + +# Builds the confirm question string from resources about to be consumed to upgrade tokens +# @param loot_mastery_tokens Upgradable token sets +# @param needed_resources Hash of needed shards, perms and blue essence +def build_token_crafting_confirm_question(loot_mastery_tokens, needed_resources) + question_string = "Upgrade #{loot_mastery_tokens.count} champions using " + question_string += "#{needed_resources['shards']} Shards, " if needed_resources['shards'].positive? + question_string += "#{needed_resources['perms']} Permanents, " if needed_resources['perms'].positive? + question_string += "#{needed_resources['essence']} Blue Essence, " if needed_resources['essence'].positive? + + question_string = question_string.delete_suffix(', ') + "#{question_string}?" +end + +# Will redeem tokens after confirmation +# @param client Client connector +# @param loot_mastery_tokens Upgradable token sets +# @param needed_resources Hash of needed shards, perms and blue essence +def execute_token_crafting(client, loot_mastery_tokens, needed_resources) + unless ans_y.include? user_input_check( + build_token_crafting_confirm_question(loot_mastery_tokens, needed_resources), + ans_yn, + ans_yn_d, + 'confirm' + ) + return + end + + loot_mastery_tokens.each do |t| + target_level = (t['lootName'])[-1] + case t['upgrade_type'] + when 'shard' + client.req_post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withshard", + [t['lootId'], "CHAMPION_RENTAL_#{t['refId']}"], + 1 + ) + when 'permanent' + client.req_post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withpermanent", + [t['lootId'], "CHAMPION_#{t['refId']}"], + 1 + ) + when 'essence' + client.req_post_recipe( + "CHAMPION_TOKEN_#{target_level}_redeem_withessence", + [t['lootId'], 'CURRENCY_champion'], + 1 + ) + end + + client.stat_tracker.add_redeemed(1) + end +end diff --git a/src/modules/handlers/materials.rb b/src/modules/handlers/materials.rb index 2ae902a..21ae9f1 100644 --- a/src/modules/handlers/materials.rb +++ b/src/modules/handlers/materials.rb @@ -1,75 +1,9 @@ # frozen_string_literal: true -require_relative 'materials/capsules' -require_relative 'materials/key_fragments' -require_relative 'materials/mastery_tokens' -require_relative 'materials/mythic_essence' +require_relative '../../class/menu/materials_menu' -# Handles anything from the 'materials' loot section like keys, capsules and more +# Wrapper for materials # @param client Client connector def handle_materials(client) - done = false - things_done = [] - - until done - todo = user_input_check( - "\nWhat would you like to do?\n\n".light_cyan + - materials_todo_str(things_done) + ' Option: '.white, - materials_todo.keys, - '', - '' - ) - - things_done << todo - puts separator + "\n\nOption chosen: #{materials_todo[todo]}".light_white - done = handle_material_option(todo, client) - puts separator - end + MaterialsMenu.new(client).run_loop end - -# Returns a hash with all possible Options and their menu item names -def materials_todo - { - '1' => 'Mythic Essence', - '2' => 'Key Fragments', - '3' => 'Capsules', - '4' => 'Mastery Tokens', - 'x' => 'Back to main menu' - } -end - -# Outputs things to do as a pretty string, done things are marked accordingly -# @param things_done Options already processed -def materials_todo_str(things_done) - todo_string = '' - materials_todo.each do |k, v| - todo_string += "[#{k}] ".light_white - todo_string += if things_done.include? k - "#{v} (done)\n".light_green - else - "#{v}\n".light_cyan - end - end - - todo_string -end - -# Calls the corresponding material method -# @param thing_todo Option name -# @return true if done -def handle_material_option(thing_todo, client) - case thing_todo - when '1' - handle_mythic_essence(client) - when '2' - handle_key_fragments(client) - when '3' - handle_capsules(client) - when '4' - handle_mastery_tokens(client) - when 'x' - return true - end - - false -end \ No newline at end of file diff --git a/src/modules/handlers/materials/mastery_tokens.rb b/src/modules/handlers/materials/mastery_tokens.rb deleted file mode 100644 index fa7ea53..0000000 --- a/src/modules/handlers/materials/mastery_tokens.rb +++ /dev/null @@ -1,134 +0,0 @@ -# frozen_string_literal: true - -# Redeems mastery 6/7 tokens as efficiently as possible: -# Prefers champion shard over champion permanent over blue essence -# @param client Client connector -def handle_mastery_tokens(client) - player_loot = client.req_get_player_loot - - loot_shards = player_loot.select { |l| l['type'] == 'CHAMPION_RENTAL' } - loot_perms = player_loot.select { |l| l['type'] == 'CHAMPION' } - - recipes6 = client.req_get_recipes_for_item('CHAMPION_TOKEN_6-1') - recipes7 = client.req_get_recipes_for_item('CHAMPION_TOKEN_7-1') - - recipe6_cost = recipes6.select do |r| - r['recipeName'] == 'CHAMPION_TOKEN_6_redeem_withessence' - end - recipe6_cost = recipe6_cost[0]['slots'][1]['quantity'] - recipe7_cost = recipes7.select do |r| - r['recipeName'] == 'CHAMPION_TOKEN_7_redeem_withessence' - end - recipe7_cost = recipe7_cost[0]['slots'][1]['quantity'] - - loot_overall_tokens = - player_loot.select do |l| - (l['lootName'] == 'CHAMPION_TOKEN_6') || - (l['lootName'] == 'CHAMPION_TOKEN_7') - end - - puts "Found #{count_loot_items(loot_overall_tokens)} Mastery Tokens.".light_blue - - loot_mastery_tokens = - player_loot.select do |l| - (l['lootName'] == 'CHAMPION_TOKEN_6' && l['count'] == 2) || - (l['lootName'] == 'CHAMPION_TOKEN_7' && l['count'] == 3) - end - - if loot_mastery_tokens.count.zero? - puts 'Found no upgradable set of Mastery Tokens.'.yellow - return - end - - loot_mastery_tokens = - loot_mastery_tokens.sort_by { |l| [l['lootName'], l['itemDesc']] } - puts "We could upgrade the following champions:\n".light_blue - needed_shards = 0 - needed_perms = 0 - needed_essence = 0 - - loot_mastery_tokens.each do |t| - ref_shard = - loot_shards.select { |l| t['refId'] == l['storeItemId'].to_s } - ref_perm = - loot_perms.select { |l| t['refId'] == l['storeItemId'].to_s } - - print pad(t['itemDesc'], 15, false).light_white - print ' to Mastery Level '.light_black - print t['lootName'][-1].light_white - print ' using '.light_black - - if !ref_shard.empty? && ref_shard[0]['count'].positive? - print 'a champion shard.'.green - needed_shards += 1 - t['upgrade_type'] = 'shard' - elsif !ref_perm.empty? && ref_shard[0]['count'].positive? - print 'a champion permanent.'.green - needed_perms += 1 - t['upgrade_type'] = 'permanent' - else - recipe_cost = (t['lootName'])[-1] == '6' ? recipe6_cost : recipe7_cost - print "#{recipe_cost} Blue Essence.".yellow - needed_essence += recipe_cost - t['upgrade_type'] = 'essence' - end - - puts - end - puts - - owned_essence = - player_loot.select { |l| l['lootId'] == 'CURRENCY_champion' } - owned_essence = owned_essence[0]['count'] - - if owned_essence < needed_essence - puts "You're missing #{needed_essence - owned_essence} Blue Essence needed to proceed. Skipping...".yellow - return - end - - question_string = - "Upgrade #{loot_mastery_tokens.count} champions using " - question_string += "#{needed_shards} Shards, " if needed_shards.positive? - question_string += "#{needed_perms} Permanents, " if needed_perms.positive? - if needed_essence.positive? - question_string += - "#{needed_essence} Blue Essence, " - end - question_string = question_string.delete_suffix(', ') - question_string += '?' - - if ans_y.include? user_input_check( - question_string, - ans_yn, - ans_yn_d, - 'confirm' - ) - loot_mastery_tokens.each do |t| - target_level = (t['lootName'])[-1] - case t['upgrade_type'] - when 'shard' - client.req_post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withshard", - [t['lootId'], "CHAMPION_RENTAL_#{t['refId']}"], - 1 - ) - when 'permanent' - client.req_post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withpermanent", - [t['lootId'], "CHAMPION_#{t['refId']}"], - 1 - ) - when 'essence' - client.req_post_recipe( - "CHAMPION_TOKEN_#{target_level}_redeem_withessence", - [t['lootId'], 'CURRENCY_champion'], - 1 - ) - end - - client.stat_tracker.add_redeemed(1) - end - end -rescue StandardError => e - handle_exception(e, 'token upgrades') -end diff --git a/src/modules/handlers/materials/mythic_essence.rb b/src/modules/handlers/materials/mythic_essence.rb deleted file mode 100644 index 4649869..0000000 --- a/src/modules/handlers/materials/mythic_essence.rb +++ /dev/null @@ -1,118 +0,0 @@ -# frozen_string_literal: true - -# Handles mythic essence crafting -# @param client Client connector -def handle_mythic_essence(client) - player_loot = client.req_get_player_loot - mythic_loot_id = 'CURRENCY_mythic' - - loot_essence = player_loot.select { |l| l['lootId'] == mythic_loot_id } - loot_essence = loot_essence[0] - - if loot_essence.nil? || loot_essence['count'].zero? - puts 'Found no Mythic Essence to use.'.yellow - return - end - - puts "Found #{loot_essence['count']} Mythic Essence.".light_blue - craft_mythic_type_names = [ - 'Blue Essence', - 'Orange Essence', - 'Random Skin Shards' - ] - - craft_mythic_type = - user_input_check( - "Okay, what would you like to craft?\n" \ - "[1] #{craft_mythic_type_names[0]}\n" \ - "[2] #{craft_mythic_type_names[1]}\n" \ - "[3] #{craft_mythic_type_names[2]}\n" \ - "[x] Cancel\n", - %w[1 2 3 x], - '[1|2|3|x]' - ) - - case craft_mythic_type - # Blue Essence, Orange Essence, Random Skin Shard - when '1' - recipe_target = 'CURRENCY_champion' - when '2' - recipe_target = 'CURRENCY_cosmetic' - when '3' - recipe_target = 'CHEST_291' - when 'x' - puts 'Mythic crafting canceled.'.yellow - return - else - puts 'Invalid state, exiting.'.yellow - return - end - - recipes = client.req_get_recipes_for_item(mythic_loot_id) - recipes = - recipes.select { |r| r['outputs'][0]['lootName'] == recipe_target } - - if recipes.empty? - puts "Recipes for #{craft_mythic_type_names[craft_mythic_type.to_i - 1]} seem to be unavailable.".yellow - return - end - recipe = recipes[0] - - puts "Recipe found: #{recipe['contextMenuText']} for " \ - "#{recipe['slots'][0]['quantity']} Mythic Essence".light_blue - - craft_mythic_amount = - user_input_check( - 'Alright, how much Mythic Essence should we use to craft ' \ - "#{craft_mythic_type_names[craft_mythic_type.to_i - 1]}?", - (1..loot_essence['count'].to_i) - .to_a - .append('all') - .append('x') - .map!(&:to_s), - "[1..#{loot_essence['count']}|all|x]" - ) - - if craft_mythic_amount == 'x' - puts 'Mythic crafting canceled.'.yellow - return - end - craft_mythic_amount = loot_essence['count'] if craft_mythic_amount == 'all' - craft_mythic_amount = craft_mythic_amount.to_i - - could_craft = - (craft_mythic_amount / recipe['slots'][0]['quantity']).floor - - if could_craft.zero? - puts 'Not enough Mythic Essence for that recipe.'.yellow - return - end - - craft_quantity = could_craft * recipe['outputs'][0]['quantity'] - craft_target_name = craft_mythic_type_names[craft_mythic_type.to_i - 1] - craft_price = could_craft * recipe['slots'][0]['quantity'] - - if ans_y.include? user_input_check( - "Craft #{craft_quantity} #{craft_target_name} from #{craft_price} Mythic Essence?", - ans_yn, - ans_yn_d, - 'confirm' - ) - case craft_mythic_type - when '1' - client.stat_tracker.add_blue_essence(craft_quantity) - when '2' - client.stat_tracker.add_orange_essence(craft_quantity) - end - client.stat_tracker.add_crafted(could_craft) - - client.req_post_recipe( - recipe['recipeName'], - mythic_loot_id, - (craft_mythic_amount / recipe['slots'][0]['quantity']).floor - ) - puts 'Done!'.green - end -rescue StandardError => e - handle_exception(e, 'Mythic Essence') -end diff --git a/src/modules/handlers/mythic_essence.rb b/src/modules/handlers/mythic_essence.rb new file mode 100644 index 0000000..a831ded --- /dev/null +++ b/src/modules/handlers/mythic_essence.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require_relative '../../class/dictionary' +require_relative '../../class/menu/mythic_menu' + +# Handles mythic essence crafting +# @param client Client connector +def handle_mythic_essence(client) + loot_essence = client.req_get_player_loot.select { |l| l['lootId'] == Dictionary::MYTHIC_ESSENCE }[0] + + if loot_essence.nil? || loot_essence['count'].zero? + puts 'Found no Mythic Essence to use.'.yellow + return + end + + puts "Found #{loot_essence['count']} Mythic Essence.".light_blue + + # Determines what to craft + mythic_menu = MythicMenu.new(client) + bail = mythic_menu.run_loop + return if bail + + craft_target_name = mythic_menu.things_todo[mythic_menu.thing_todo] + craft_amount = determine_mythic_craft_amount(craft_target_name, mythic_menu.recipe, loot_essence['count']) + + if craft_amount.zero? + puts 'Not enough Mythic Essence for that.'.yellow + return + end + + execute_mythic_crafting(client, craft_target_name, mythic_menu.recipe, craft_amount) + puts 'Done!'.green +rescue StandardError => e + handle_exception(e, 'Mythic Essence') +end + +# Calculates how the amount of things that can be crafted with user-specified mythic essence +# @param target_name What the thing is called (e.g. Blue Essence, Random Skin Shards, ...) +# @param recipe Recipe to determine cost per unit +# @param essence_owned Owned mythic essence (upper limit for user selection) +def determine_mythic_craft_amount(target_name, recipe, essence_owned) + craft_mythic_amount = user_input_check( + 'Alright, how much Mythic Essence should we use to craft ' \ + "#{target_name}?", + (1..essence_owned.to_i) + .to_a + .append('all') + .append('x') + .map!(&:to_s), + "[1..#{essence_owned}|all|x]" + ) + + if craft_mythic_amount == 'x' + puts 'Mythic crafting canceled.'.yellow + return + end + craft_mythic_amount = essence_owned if craft_mythic_amount == 'all' + craft_mythic_amount = craft_mythic_amount.to_i + + (craft_mythic_amount / recipe['slots'][0]['quantity']).floor +end + +# Confirms and executes crafting +# @param client Client connector +# @param target_name Thing to craft +# @param recipe Recipe to craft with +# @param craft_amount How many things to craft +def execute_mythic_crafting(client, target_name, recipe, craft_amount) + craft_quantity = craft_amount * recipe['outputs'][0]['quantity'] + craft_price = craft_amount * recipe['slots'][0]['quantity'] + + unless ans_y.include? user_input_check( + "Craft #{craft_quantity} #{target_name} from #{craft_price} Mythic Essence?", + ans_yn, + ans_yn_d, + 'confirm' + ) + return + end + + case recipe['outputs'][0]['lootName'] + when Dictionary::BLUE_ESSENCE + client.stat_tracker.add_blue_essence(craft_quantity) + when Dictionary::ORANGE_ESSENCE + client.stat_tracker.add_orange_essence(craft_quantity) + end + client.stat_tracker.add_crafted(craft_amount) + + client.req_post_recipe( + recipe['recipeName'], + Dictionary::MYTHIC_ESSENCE, + craft_amount + ) +end diff --git a/src/modules/handlers/skins.rb b/src/modules/handlers/skins.rb index 2490df7..7a343e8 100644 --- a/src/modules/handlers/skins.rb +++ b/src/modules/handlers/skins.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative 'generic_loot' + # Wrapper for skin shards and permanents # @param client Client connector def handle_skins(client) diff --git a/src/modules/handlers/tacticians.rb b/src/modules/handlers/tacticians.rb index 741e05c..011c263 100644 --- a/src/modules/handlers/tacticians.rb +++ b/src/modules/handlers/tacticians.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative 'generic_loot' + # Wrapper for tacticians # @param client Client connector # @note There are no shards for tacticians, only permanents diff --git a/src/modules/handlers/wards.rb b/src/modules/handlers/wards.rb index 5c4c598..b43ee78 100644 --- a/src/modules/handlers/wards.rb +++ b/src/modules/handlers/wards.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative 'generic_loot' + # Wrapper for ward skins and their permanents # @param client Client connector def handle_ward_skins(client) diff --git a/src/modules/open_url.rb b/src/modules/open_url.rb index bed1eaf..8eb2334 100644 --- a/src/modules/open_url.rb +++ b/src/modules/open_url.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'launchy' + def open_github puts 'Opening GitHub repository at https://github.com/marvinscham/disenchanter/ in your browser...'.light_blue Launchy.open('https://github.com/marvinscham/disenchanter/') diff --git a/src/modules/update/checker.rb b/src/modules/update/checker.rb index c513df8..0d3660c 100644 --- a/src/modules/update/checker.rb +++ b/src/modules/update/checker.rb @@ -41,7 +41,8 @@ def download_remote_version(version) ans_yn, ans_yn_d ) - `curl https://github.com/marvinscham/disenchanter/releases/download/#{version}/disenchanter_up.exe -L -o disenchanter_up.exe` + exe_url = "https://github.com/marvinscham/disenchanter/releases/download/#{version}/disenchanter_up.exe" + `curl #{exe_url} -L -o disenchanter_up.exe` puts 'Done downloading!'.green pid = spawn('start cmd.exe @cmd /k "disenchanter_up.exe"') @@ -49,4 +50,4 @@ def download_remote_version(version) puts 'Exiting...'.light_black exit end -end \ No newline at end of file +end diff --git a/src/modules/user_input.rb b/src/modules/user_input.rb index 39b325f..0c3caa3 100644 --- a/src/modules/user_input.rb +++ b/src/modules/user_input.rb @@ -11,11 +11,11 @@ def user_input_check(question, answers, answer_display, color_preset = 'default' case color_preset when 'confirm' - question = - "CONFIRM: #{question} ".light_magenta + answer_display.to_s.light_white + ': '.light_magenta + question = "CONFIRM: #{question} ".light_magenta + answer_display.to_s.light_white + ': '.light_magenta when 'default' - question = - question.light_cyan + "#{answer_display}: ".light_white + question += " #{answer_display}: ".light_white + when 'dry' + question += " #{answer_display} (DRY): ".light_red end until answers.include? input From 80c1518594e679acd326b7b63104bf21063603ff Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sat, 17 Feb 2024 05:05:53 +0100 Subject: [PATCH 57/63] Loosened RuboCop rigor --- .rubocop.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 55e0c33..f944e20 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,14 +4,15 @@ Layout/EndOfLine: EnforcedStyle: lf Metrics/MethodLength: CountAsOne: ['array', 'method_call', 'hash'] - Max: 30 + Max: 35 Severity: refactor Metrics/CyclomaticComplexity: - Max: 12 + Max: 15 Severity: refactor Metrics/PerceivedComplexity: - Max: 12 + Max: 15 Severity: refactor Metrics/AbcSize: - Max: 25 + CountRepeatedAttributes: false + Max: 30 Severity: refactor From 0ded27635842f5a348c039434c01703966a3e2cc Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sat, 17 Feb 2024 05:06:16 +0100 Subject: [PATCH 58/63] Swapped from ocra to ocran --- Gemfile | 4 ++-- Gemfile.lock | 4 ++-- scripts/build_main.sh | 9 +-------- scripts/build_updater.sh | 9 +-------- 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/Gemfile b/Gemfile index dc8ca7a..c379a80 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,5 @@ # frozen_string_literal: true + source 'https://rubygems.org' git_source(:github) { |_repo| 'https://github.com/marvinscham/disenchanter.git' } ruby '3.2.3' @@ -12,10 +13,9 @@ gem 'win32-shortcut', '~> 0.3.0' group :development do # Builds windows executable - gem 'ocra', '1.3.11', require: false + gem 'ocran', '1.3.15', require: false # Ruby formatter, config in .rufo gem 'rufo', '>= 0.13.0', require: false # Ruby linter, config in .rubocop gem 'rubocop', '~> 1.60', require: false end - diff --git a/Gemfile.lock b/Gemfile.lock index 4ca517e..49f253e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,7 +11,7 @@ GEM language_server-protocol (3.17.0.3) launchy (2.5.2) addressable (~> 2.8) - ocra (1.3.11) + ocran (1.3.15) open-uri (0.2.0) stringio time @@ -57,7 +57,7 @@ DEPENDENCIES colorize (~> 1.1) json (~> 2.6) launchy (~> 2.5) - ocra (= 1.3.11) + ocran (= 1.3.15) open-uri (~> 0.2.0) rubocop (~> 1.60) rufo (>= 0.13.0) diff --git a/scripts/build_main.sh b/scripts/build_main.sh index 6407c4f..c9f68b7 100644 --- a/scripts/build_main.sh +++ b/scripts/build_main.sh @@ -2,15 +2,8 @@ mkdir -p build touch ./build/.build.lockfile -ocra src/main.rb \ +ocran src/main.rb \ --gemfile ./Gemfile \ - --dll ruby_builtin_dlls/libffi-7.dll \ - --dll ruby_builtin_dlls/libssp-0.dll \ - --dll ruby_builtin_dlls/libgmp-10.dll \ - --dll ruby_builtin_dlls/libgcc_s_seh-1.dll \ - --dll ruby_builtin_dlls/libwinpthread-1.dll \ - --dll ruby_builtin_dlls/libssl-1_1-x64.dll \ - --dll ruby_builtin_dlls/libcrypto-1_1-x64.dll \ --icon ./assets/BE_icon.ico \ --output ./build/disenchanter.exe diff --git a/scripts/build_updater.sh b/scripts/build_updater.sh index eb35e45..a88c27f 100644 --- a/scripts/build_updater.sh +++ b/scripts/build_updater.sh @@ -2,15 +2,8 @@ mkdir -p build touch ./build/.build.lockfile -ocra src/updater.rb \ +ocran src/updater.rb \ --gemfile ./Gemfile \ - --dll ruby_builtin_dlls/libffi-7.dll \ - --dll ruby_builtin_dlls/libssp-0.dll \ - --dll ruby_builtin_dlls/libgmp-10.dll \ - --dll ruby_builtin_dlls/libgcc_s_seh-1.dll \ - --dll ruby_builtin_dlls/libwinpthread-1.dll \ - --dll ruby_builtin_dlls/libssl-1_1-x64.dll \ - --dll ruby_builtin_dlls/libcrypto-1_1-x64.dll \ --icon BE_icon.ico \ --output ./build/disenchanter_up.exe From f3146835ad41c1339166f0cd2370223b20733479 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sat, 17 Feb 2024 05:14:44 +0100 Subject: [PATCH 59/63] Took off training wheels, some fixes --- src/class/client.rb | 18 +++++++++--------- src/modules/stat_submission.rb | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/class/client.rb b/src/class/client.rb index b284163..e1a648a 100644 --- a/src/class/client.rb +++ b/src/class/client.rb @@ -55,19 +55,19 @@ def request_get(path) end end - def request_post(path, _body) + def request_post(path, body) puts "Posting against #{host}/#{path}".light_black if @debug puts 'DRY RUN ENABLED - actually did nothing'.light_red if @dry_run return if @dry_run - # create_client do |http| - # uri = URI("#{host}/#{path}") - # req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') - # req.body = body - # req_set_headers(req) - # res = http.request req - # JSON.parse(res.body) - # end + create_client do |http| + uri = URI("#{host}/#{path}") + req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') + req.body = body + req_set_headers(req) + res = http.request req + JSON.parse(res.body) + end end def refresh_loot diff --git a/src/modules/stat_submission.rb b/src/modules/stat_submission.rb index dd28a09..51f312e 100644 --- a/src/modules/stat_submission.rb +++ b/src/modules/stat_submission.rb @@ -5,12 +5,12 @@ def submit_stats(stat_tracker) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE - http.request(build_stat_request(stat_tracker)) + http.request(build_stat_request(uri, stat_tracker)) rescue StandardError => e handle_exception(e, 'stat submission') end -def build_stat_request(stat_tracker) +def build_stat_request(uri, stat_tracker) req = Net::HTTP::Post.new(uri, 'Content-Type': 'application/json') req.body = { a: stat_tracker.actions, d: stat_tracker.disenchanted, From 1c74fd8bd09b2dd0f4ebfddb19d8d245d7a839cc Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sat, 17 Feb 2024 05:52:08 +0100 Subject: [PATCH 60/63] Build fixes --- Gemfile | 1 + Gemfile.lock | 2 ++ scripts/build_updater.sh | 2 +- src/modules/update/checker.rb | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index c379a80..8ecfa9d 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,7 @@ gem 'base64', '~> 0.2' gem 'colorize', '~> 1.1' gem 'json', '~> 2.6' gem 'launchy', '~> 2.5' +gem 'openssl', '= 3.1.0' gem 'open-uri', '~> 0.2.0' gem 'win32-shortcut', '~> 0.3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 49f253e..738ae96 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,6 +16,7 @@ GEM stringio time uri + openssl (3.1.0) parallel (1.24.0) parser (3.3.0.5) ast (~> 2.4.1) @@ -59,6 +60,7 @@ DEPENDENCIES launchy (~> 2.5) ocran (= 1.3.15) open-uri (~> 0.2.0) + openssl (= 3.1.0) rubocop (~> 1.60) rufo (>= 0.13.0) win32-shortcut (~> 0.3.0) diff --git a/scripts/build_updater.sh b/scripts/build_updater.sh index a88c27f..af8d6b2 100644 --- a/scripts/build_updater.sh +++ b/scripts/build_updater.sh @@ -4,7 +4,7 @@ touch ./build/.build.lockfile ocran src/updater.rb \ --gemfile ./Gemfile \ - --icon BE_icon.ico \ + --icon ./assets/BE_icon.ico \ --output ./build/disenchanter_up.exe rm ./build/.build.lockfile diff --git a/src/modules/update/checker.rb b/src/modules/update/checker.rb index 0d3660c..e42c12b 100644 --- a/src/modules/update/checker.rb +++ b/src/modules/update/checker.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'openssl' + def check_update(version_local) tag_name = grab_remote_tag_name From da894fc50b99535d48ec2110bee3c3a5c7b9a5fc Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sat, 17 Feb 2024 06:06:02 +0100 Subject: [PATCH 61/63] Added Mastery Chart shortcut --- src/class/menu/main_menu.rb | 3 +++ src/modules/open_url.rb | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/class/menu/main_menu.rb b/src/class/menu/main_menu.rb index baac901..ab40401 100644 --- a/src/class/menu/main_menu.rb +++ b/src/class/menu/main_menu.rb @@ -29,6 +29,7 @@ def initialize(client) '6' => 'Emotes', '7' => 'Ward Skins', '8' => 'Icons', + 'm' => 'Open Mastery Chart profile', 's' => 'Open Disenchanter Global Stats', 'r' => 'Open GitHub repository', 'd' => 'Debug Tools', @@ -57,6 +58,8 @@ def handle_option(todo) handle_ward_skins(@client) when '8' handle_icons(@client) + when 'm' + open_masterychart(@client) when 's' open_stats when 'r' diff --git a/src/modules/open_url.rb b/src/modules/open_url.rb index 8eb2334..9e59a59 100644 --- a/src/modules/open_url.rb +++ b/src/modules/open_url.rb @@ -11,3 +11,11 @@ def open_stats puts 'Opening Global Stats at https://github.com/marvinscham/disenchanter/wiki/Stats in your browser...'.light_blue Launchy.open('https://github.com/marvinscham/disenchanter/wiki/Stats') end + +def open_masterychart(client) + server = ask("Which server do you play on (EUW/NA/BR/TR...)?\n".light_cyan) + player = client.req_get_current_summoner + url = "https://masterychart.com/profile/#{server}/#{player['gameName']}-#{player['tagLine']}?ref=disenchanter" + puts "Opening your profile at #{url} in your browser...".light_blue + Launchy.open(url) +end From 54e75e51d6ae2a99d03536253b2cc02ac678df98 Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sat, 17 Feb 2024 06:15:50 +0100 Subject: [PATCH 62/63] Updated changelog --- CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7200192..589845d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,19 @@ # Changelog All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Also see the [versioning strategy](./VERSIONING.md). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Related: [versioning strategy](./VERSIONING.md). -## v1.6.0 - next +## v1.6.0 - Feb 17, 2024 ### Added +- #35 Disenchanter can be started from anywhere now + - It will try to find your League Client via registry > start menu > default path > locally - #57 Rerolling owned esports emotes that cannot be disenchanted - #19 Tacticians can now be disenchanted -- #27 Proper changelog and versioning files +- Shortcut to your [Mastery Chart](https://masterychart.com) profile +- #27 Proper changelog and versioning info ### Changed - (dev) Code is split in modules now instead of stuffed into a single script +- (dev) Switched from ocra to ocran for building the executable ### Fixed - #142 Script won't crash if you don't have a summoner name - Essentially Riot ID support From df301498653ce350064b43f54e2dfabeb0d0cb0b Mon Sep 17 00:00:00 2001 From: marvinscham Date: Sat, 17 Feb 2024 06:44:38 +0100 Subject: [PATCH 63/63] Added default cases --- .rubocop.yml | 2 ++ src/class/menu/champions_menu.rb | 2 ++ src/class/menu/debug_menu.rb | 2 ++ src/class/menu/main_menu.rb | 2 ++ src/class/menu/materials_menu.rb | 2 ++ src/class/menu/mythic_menu.rb | 2 ++ src/modules/handlers/champions_mastery.rb | 2 ++ src/modules/handlers/generic_loot.rb | 4 ++++ src/modules/handlers/mastery_tokens.rb | 2 ++ src/modules/handlers/mythic_essence.rb | 2 ++ src/modules/user_input.rb | 4 ++-- 11 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index f944e20..d18ff8a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,3 +16,5 @@ Metrics/AbcSize: CountRepeatedAttributes: false Max: 30 Severity: refactor +Style/EmptyElse: + AllowComments: true \ No newline at end of file diff --git a/src/class/menu/champions_menu.rb b/src/class/menu/champions_menu.rb index b881281..db6e060 100644 --- a/src/class/menu/champions_menu.rb +++ b/src/class/menu/champions_menu.rb @@ -44,6 +44,8 @@ def handle_option(thing_todo) @loot_shards = handle_champions_mastery(@client, @loot_shards, keep_all: true) when '5' @loot_shards = handle_champions_collection(@loot_shards) + else + return false end @loot_shards = @loot_shards.select { |l| l['count'].positive? } diff --git a/src/class/menu/debug_menu.rb b/src/class/menu/debug_menu.rb index 60075ca..9d4399a 100644 --- a/src/class/menu/debug_menu.rb +++ b/src/class/menu/debug_menu.rb @@ -42,6 +42,8 @@ def handle_option(thing_todo) puts @client.debug ? 'Debug mode enabled' : 'Debug mode disabled' when 'x' return true + else + return false end false diff --git a/src/class/menu/main_menu.rb b/src/class/menu/main_menu.rb index ab40401..a99e2d0 100644 --- a/src/class/menu/main_menu.rb +++ b/src/class/menu/main_menu.rb @@ -68,6 +68,8 @@ def handle_option(todo) handle_debug(@client) when 'x' return true + else + return false end @client.refresh_loot diff --git a/src/class/menu/materials_menu.rb b/src/class/menu/materials_menu.rb index cb69dd1..36ebe71 100644 --- a/src/class/menu/materials_menu.rb +++ b/src/class/menu/materials_menu.rb @@ -38,6 +38,8 @@ def handle_option(thing_todo) handle_mastery_tokens(@client) when 'x' return true + else + return false end false diff --git a/src/class/menu/mythic_menu.rb b/src/class/menu/mythic_menu.rb index 40c1ee1..c40cab3 100644 --- a/src/class/menu/mythic_menu.rb +++ b/src/class/menu/mythic_menu.rb @@ -35,6 +35,8 @@ def handle_option(thing_todo) thing_to_craft = Dictionary::RANDOM_SKIN_SHARD when 'x' return true + else + return false end recipes = @client.req_get_recipes_for_item(Dictionary::MYTHIC_ESSENCE) diff --git a/src/modules/handlers/champions_mastery.rb b/src/modules/handlers/champions_mastery.rb index 3d0a788..3fa7212 100644 --- a/src/modules/handlers/champions_mastery.rb +++ b/src/modules/handlers/champions_mastery.rb @@ -28,6 +28,8 @@ def handle_champions_mastery(client, loot_shards, keep_all: false) mastery6_champion_ids << m['championId'] when level_threshold..5 threshold_champion_ids << m['championId'] + else + # Nothing to do end end diff --git a/src/modules/handlers/generic_loot.rb b/src/modules/handlers/generic_loot.rb index 26cc318..c62a6dd 100644 --- a/src/modules/handlers/generic_loot.rb +++ b/src/modules/handlers/generic_loot.rb @@ -76,6 +76,10 @@ def handle_generic_owned(loot_generic, name) when 'y' loot_generic = loot_generic.select { |g| g['redeemableStatus'] == 'ALREADY_OWNED' } puts "Filtered to #{count_loot_items(loot_generic)} items.".light_blue + when 'n' + # Nothing to do + else + raise StandardError, "This shouldn't be possible yet here we are." end end diff --git a/src/modules/handlers/mastery_tokens.rb b/src/modules/handlers/mastery_tokens.rb index fd30b14..5e4c761 100644 --- a/src/modules/handlers/mastery_tokens.rb +++ b/src/modules/handlers/mastery_tokens.rb @@ -153,6 +153,8 @@ def execute_token_crafting(client, loot_mastery_tokens, needed_resources) [t['lootId'], 'CURRENCY_champion'], 1 ) + else + # Weird, do nothing. end client.stat_tracker.add_redeemed(1) diff --git a/src/modules/handlers/mythic_essence.rb b/src/modules/handlers/mythic_essence.rb index a831ded..c6d526f 100644 --- a/src/modules/handlers/mythic_essence.rb +++ b/src/modules/handlers/mythic_essence.rb @@ -83,6 +83,8 @@ def execute_mythic_crafting(client, target_name, recipe, craft_amount) client.stat_tracker.add_blue_essence(craft_quantity) when Dictionary::ORANGE_ESSENCE client.stat_tracker.add_orange_essence(craft_quantity) + else + # Nothing to track here end client.stat_tracker.add_crafted(craft_amount) diff --git a/src/modules/user_input.rb b/src/modules/user_input.rb index 0c3caa3..eec30ff 100644 --- a/src/modules/user_input.rb +++ b/src/modules/user_input.rb @@ -12,10 +12,10 @@ def user_input_check(question, answers, answer_display, color_preset = 'default' case color_preset when 'confirm' question = "CONFIRM: #{question} ".light_magenta + answer_display.to_s.light_white + ': '.light_magenta - when 'default' - question += " #{answer_display}: ".light_white when 'dry' question += " #{answer_display} (DRY): ".light_red + else + question += " #{answer_display}: ".light_white end until answers.include? input