rs_api 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4302d947223389c4bb1e40b4c804a2b0b42e2ef770bc3277a2892acd9a539a21
4
+ data.tar.gz: '0082b0d7bae37f560efc69a95e254916e1e043f4a966d858fab1e8544659dee3'
5
+ SHA512:
6
+ metadata.gz: b0c78ca01a89467c0f76387fdfdba7e1dc79ea8ec2fd28db5798a514ecbe22aa8bdd3a32108c6da83a0be66a5933d0bfabab0bb867700d961c459cf66ddfa64b
7
+ data.tar.gz: 8d4e0c6fbf0e8d59bd8be9c7199316c57966544972671ee7181cca7be204e2a0ebf76070a1cceabd7d7428409569727cbb72f656e7b3073799addade8b05b0b0
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ Config.setup do |config|
4
+ env = ENV['RS_API_ENV'] || 'development'
5
+
6
+ # Set preferred settings
7
+ config.const_name = 'Settings'
8
+ config.evaluate_erb_in_yaml = true
9
+
10
+ # Load Settings
11
+ config.load_and_set_settings(Config.setting_files('config', env))
12
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RsApi
4
+ # Functions for helping with Runescape player names
5
+ module PlayerNameHelper
6
+ class RsNameInvalid < StandardError; end
7
+
8
+ def self.check_player_name(player_name)
9
+ raise player_name_invalid_error unless valid_player_name(player_name)
10
+ end
11
+
12
+ class << self
13
+ private
14
+
15
+ def valid_player_name(player_name)
16
+ (player_name =~ /^[a-zA-Z0-9\s]{1,12}$/)&.zero?
17
+ end
18
+
19
+ def player_name_invalid_error
20
+ RsNameInvalid.new('Please enter a 1-12 character alphanumeric name')
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal:true
2
+
3
+ # Default Runescape-related constants
4
+ module RsApi
5
+ module SkillHelper
6
+ MAX = 13_034_431 # Xp needed to lv 99 in any skill,
7
+ # useless now BUT could be useful in future.
8
+
9
+ # Mapping of received info from rs_api's uri.
10
+ SKILL_ID_CONST = {
11
+ 0 => :overall,
12
+ 1 => :attack,
13
+ 2 => :defence,
14
+ 3 => :strength,
15
+ 4 => :constitution,
16
+ 5 => :ranged,
17
+ 6 => :prayer,
18
+ 7 => :magic,
19
+ 8 => :cooking,
20
+ 9 => :woodcutting,
21
+ 10 => :fletching,
22
+ 11 => :fishing,
23
+ 12 => :firemaking,
24
+ 13 => :crafting,
25
+ 14 => :smithing,
26
+ 15 => :mining,
27
+ 16 => :herblore,
28
+ 17 => :agility,
29
+ 18 => :thieving,
30
+ 19 => :slayer,
31
+ 20 => :farming,
32
+ 21 => :runecrafting,
33
+ 22 => :hunter,
34
+ 23 => :construction,
35
+ 24 => :summoning,
36
+ 25 => :dungeoneering,
37
+ 26 => :divination,
38
+ 27 => :invention,
39
+ 28 => :archaeology,
40
+ 29 => :necromancy
41
+ }.freeze
42
+
43
+ MONTHLY_XP_SKILL_ID_CONST = SKILL_ID_CONST.select { |k, _v| k.positive? }
44
+ .transform_keys { |k| k - 1 }.freeze
45
+ end
46
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Uncomment below to run Examples in file. Continue for other related files
4
+ # require_relative '../helpers/player_name_helper'
5
+ # require_relative '../helpers/skill_helper'
6
+
7
+ module RsApi
8
+ # Base class for getting Runescape hiscore data for a player
9
+ class Hiscore
10
+ include PlayerNameHelper
11
+ include SkillHelper
12
+
13
+ private
14
+
15
+ def colour?
16
+ Settings.colour_output
17
+ end
18
+
19
+ def display?
20
+ Settings.display_output
21
+ end
22
+
23
+ def params
24
+ raise 'implement me!'
25
+ end
26
+
27
+ def table
28
+ @table ||= Text::Table.new(horizontal_padding: 1)
29
+ end
30
+
31
+ def url
32
+ raise 'implement me!'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal:true
2
+
3
+ # Uncomment below to run Example(s) in file. May also need to for other related files.
4
+ # require 'text-table'
5
+ # require_relative 'hiscore'
6
+ # require_relative '../rs_request'
7
+ # require_relative '../helpers/integer_helper'
8
+ # require_relative '../helpers/string_colour_helper'
9
+ # require_relative '../helpers/skill_helper'
10
+ # require_relative '../patches/text_table__cell_patch' # for color printing
11
+ # require_relative '../patches/text_table__table_patch' # for color printing
12
+ # require_relative 'rs_player_experience'
13
+
14
+ module RsApi
15
+ # class PlayerCompare that compares two PlayerExp skill experience.
16
+ class PlayerCompare < Hiscore
17
+ attr_reader :player1, :player2
18
+
19
+ def initialize(player1, player2)
20
+ @player1 = PlayerExperience.new(player1)
21
+ @player2 = PlayerExperience.new(player2)
22
+ end
23
+
24
+ def compare
25
+ SKILL_ID_CONST.each do |key, skill_name|
26
+ xp_diff = @player1.all_skill_experience[key] - @player2.all_skill_experience[key]
27
+
28
+ raw_data << compare_result(key, skill_name, xp_diff)
29
+ end
30
+ end
31
+
32
+ def display
33
+ if raw_data.empty?
34
+ compare
35
+ table.head = %w[SKILL WINNER LEVEL XP-DIFFERENCE]
36
+ table.rows = formatted_data
37
+ end
38
+
39
+ if display?
40
+ # :nocov:
41
+ colour? ? (puts colour_table) : (puts table)
42
+ # :nocov:
43
+ end
44
+ table
45
+ end
46
+
47
+ def raw_data
48
+ @raw_data ||= [] # index 0 is total exp, rest is normal skills by exp
49
+ end
50
+
51
+ private
52
+
53
+ def capitalize(value)
54
+ value.capitalize
55
+ end
56
+
57
+ # :nocov:
58
+ def colour_row(row)
59
+ if row[1] == player1.player_name
60
+ row.map(&:to_s).map(&:blue)
61
+ else
62
+ row.map(&:to_s).map(&:green)
63
+ end
64
+ end
65
+
66
+ def colour_table
67
+ c_table = table.dup
68
+ c_table.head = c_table.head.map(&:white)
69
+ c_table.rows = c_table.rows.map { |row| colour_row(row) }
70
+
71
+ c_table
72
+ end
73
+ # :nocov:
74
+
75
+ def compare_result(key, skill_name, xp_diff)
76
+ case xp_diff <=> 0
77
+ when -1 # p2 has more skill
78
+ [capitalize(skill_name), @player2.player_name, @player2.raw_data[key][1], xp_diff.abs]
79
+ when 1 # p1 has more skill
80
+ [capitalize(skill_name), @player1.player_name, @player1.raw_data[key][1], xp_diff]
81
+ else # 0
82
+ [capitalize(skill_name), 'TIE', @player2.raw_data[key][1], 0]
83
+ end
84
+ end
85
+
86
+ def formatted_data
87
+ raw_data.map { |i, j, k, l| [i, j, k, l.delimited] }
88
+ end
89
+ end
90
+ end
91
+
92
+ # Example
93
+ # a = RsApi::PlayerCompare.new('tibthedragon', 'bubba tut')
94
+ # a.display
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal:true
2
+
3
+ # Uncomment below to run Example(s) in file. May also need to for other related files.
4
+ # require 'text-table'
5
+ # require_relative 'hiscore'
6
+ # require_relative '../rs_request'
7
+ # require_relative '../helpers/integer_helper'
8
+ # require_relative '../helpers/string_colour_helper'
9
+ # require_relative '../helpers/skill_helper'
10
+ # require_relative '../patches/text_table__cell_patch' # for color printing
11
+ # require_relative '../patches/text_table__table_patch' # for color printing
12
+
13
+ module RsApi
14
+ # Class PlayerExp pulls, from Hiscores, player level/experience in each skill.
15
+ class PlayerExperience < Hiscore
16
+ attr_reader :player_name, :table_results
17
+
18
+ def initialize(player_name)
19
+ PlayerNameHelper.check_player_name(player_name)
20
+ @player_name = player_name
21
+ end
22
+
23
+ def display
24
+ fill_table_data if table.rows.empty?
25
+ if display?
26
+ # :nocov:
27
+ colour? ? (puts colour_table) : (puts table)
28
+ # :nocov:
29
+ end
30
+ table
31
+ rescue RsApi::RsRequest::PlayerNotFound
32
+ puts "#{@player_name} does not exist.".red if display?
33
+ end
34
+
35
+ def raw_data
36
+ @raw_data ||= parsed[0..29]
37
+ end
38
+
39
+ def max_skill_level
40
+ # Returns the highest skill level out of all sklls
41
+ raw_data[1..].map { |value| value[1].to_i }.max.to_s
42
+ end
43
+
44
+ def skills_at_max_level
45
+ # Returns a array containing only skills at max_skill_level
46
+ # SKILL_ID_CONST[i+1] | i is +1 because I don't want to load the overall skill totals
47
+ raw_data[1..].filter_map.with_index do |value, i|
48
+ SKILL_ID_CONST[i + 1].to_s.capitalize if value[1] == max_skill_level
49
+ end
50
+ end
51
+
52
+ def all_skill_experience
53
+ # Retuns array containing the experience for each skill
54
+ # Index of skills corresponds to RsConst::SKILL_ID_CONST
55
+ raw_data.map { |n| n.last.delete(',').to_i }
56
+ end
57
+
58
+ private
59
+
60
+ # :nocov:
61
+ def colour_table
62
+ c_table = table.dup
63
+ c_table.head = c_table.head.map(&:white)
64
+
65
+ c_table.rows = c_table.rows.map do |row|
66
+ [row[0].to_s.yellow, row[1].red, row[2].cyan, row[3].green]
67
+ end
68
+
69
+ c_table
70
+ end
71
+ # :nocov:
72
+
73
+ def fill_table_data
74
+ table.head = [player_name, 'Rank', 'Level', 'Experience']
75
+
76
+ # Future: move 'Overall' xp info to footer
77
+ raw_data.each_with_index do |value, i|
78
+ table.rows << [SKILL_ID_CONST[i].capitalize, format(value[0]), value[1], format(value[2])]
79
+ end
80
+ end
81
+
82
+ def format(value)
83
+ value.to_i.delimited
84
+ end
85
+
86
+ def url
87
+ # RsApi.load_config['runescape_urls']['hiscore_url']
88
+ Settings.runescape_urls.hiscore_url
89
+ end
90
+
91
+ def parsed
92
+ # Response is in CSV format
93
+ response = RsRequest.new(url, params).get
94
+
95
+ response.body.split("\n").map { |item| item.split(',') }
96
+ end
97
+
98
+ def params
99
+ { player: player_name }
100
+ end
101
+ end
102
+ end
103
+
104
+ # Example
105
+ # a = RsApi::PlayerExperience.new('tibthedragon')
106
+ # a.display
107
+ # puts a.max_skill_level
108
+ # puts a.skills_at_max_level
109
+ # puts a.all_skill_experience
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal:true
2
+
3
+ # Useful functions extending Integer class
4
+ class Integer
5
+ def delimited
6
+ # Adds comma's every 3 numbers, if possible, for Integer and return as String
7
+ to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # monkey-patching string class to add colors.
4
+ class String
5
+ def negative
6
+ "\033[7m#{self}\033[0m"
7
+ end
8
+
9
+ # 1 is bold, 30 is black, 47 is white bg
10
+ def black
11
+ "\033[1;30;47m#{self}\033[0m"
12
+ end
13
+
14
+ def red
15
+ "\033[31m#{self}\033[0m"
16
+ end
17
+
18
+ def green
19
+ "\033[32m#{self}\033[0m"
20
+ end
21
+
22
+ def yellow
23
+ "\033[33m#{self}\033[0m"
24
+ end
25
+
26
+ def blue
27
+ "\033[34m#{self}\033[0m"
28
+ end
29
+
30
+ def purple
31
+ "\033[35m#{self}\033[0m"
32
+ end
33
+
34
+ def cyan
35
+ "\033[36m#{self}\033[0m"
36
+ end
37
+
38
+ def white
39
+ "\033[37m#{self}\033[0m"
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Monkey patching column widths to take into account cell length not including terminal escapes
4
+ # Thank you to https://github.com/mikecarlton-illumio/text-table for solving this problem
5
+ module Text
6
+ class Table
7
+ class Cell
8
+ # visible width: without terminal escapes
9
+ def width
10
+ value.gsub(/\033\[[^m]+m/, '').length
11
+ end
12
+
13
+ def to_s
14
+ invisible = value.length - width
15
+ ([' ' * table.horizontal_padding]*2).join case align
16
+ when :left
17
+ value.ljust cell_width
18
+ value.ljust cell_width+invisible
19
+ when :right
20
+ value.rjust cell_width
21
+ value.rjust cell_width+invisible
22
+ when :center
23
+ value.center cell_width
24
+ value.center cell_width+invisible
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Monkey patching column widths to take into account cell length not including terminal escapes
4
+ # Thank you to https://github.com/mikecarlton-illumio/text-table for solving this problem
5
+ module Text
6
+ class Table
7
+ def column_widths
8
+ @column_widths ||= \
9
+ all_text_table_rows.reject {|row| row.cells == :separator}.map do |row|
10
+ row.cells.map {|cell| [(cell.width/cell.colspan.to_f).ceil] * cell.colspan}.flatten
11
+ end.transpose.map(&:max)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal:true
2
+
3
+ # Uncomment below to run Examples in file. Continue for other related files
4
+ # require 'uri'
5
+ # require 'net/http'
6
+
7
+ module RsApi
8
+ # Class RsRequest that handles any Rs Api request
9
+ class RsRequest
10
+ class PlayerNotFound < StandardError; end
11
+ class ServiceUnavailable < StandardError; end
12
+
13
+ MAX_RETRIES = 2
14
+
15
+ attr_reader :url, :params
16
+
17
+ def initialize(url, params = {})
18
+ @url = url
19
+ @params = params
20
+ end
21
+
22
+ def get
23
+ uri.query = URI.encode_www_form(@params)
24
+ response = Net::HTTP.get_response(uri)
25
+
26
+ check_for_errors(response)
27
+ response
28
+ rescue ServiceUnavailable
29
+ retries ||= 0
30
+ raise ServiceUnavailable, response.code if retries >= MAX_RETRIES
31
+
32
+ retries += 1
33
+ retry
34
+ end
35
+
36
+ def put; end
37
+
38
+ private
39
+
40
+ def check_for_errors(response)
41
+ raise PlayerNotFound, response.code if response.code == '404' && response.message == 'Not Found'
42
+ raise ServiceUnavailable, response.code if response.code == '302' && response[:location] == unavailable_url
43
+ end
44
+
45
+ def unavailable_url
46
+ Settings.runescape_urls.unavailable_url
47
+ end
48
+
49
+ def uri
50
+ @uri ||= URI(@url)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Example
56
+ # a = RsApi::RsRequest.new('https://secure.runescape.com/m=hiscore/index_lite.ws?', { player: 'tibthedragon' })
57
+ # puts a.get.response.body
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal:true
2
+
3
+ # Uncomment below to run Examples in file. Continue for other related files
4
+ # require 'json'
5
+ # require '../rs_request'
6
+ # require '../helpers/skill_helper'
7
+ # require_relative 'runemetrics'
8
+
9
+ module RsApi
10
+ # For obtaining monthly xp data for a user.
11
+ class MonthlyXp < Runemetrics
12
+ class MissingPlayerData < StandardError; end
13
+
14
+ # def display; end
15
+
16
+ def previous_monthly_xp_data
17
+ # # Last entry is this current month
18
+ # # Therefore, previous month is second to last entry
19
+ raw_data.map { |data| data.last['monthData'][-2] }
20
+ end
21
+
22
+ def raw_data
23
+ # Data is a hash of the last 12 months of data for each key skill_name
24
+ @raw_data ||= request_all_data
25
+ end
26
+
27
+ private
28
+
29
+ def request_all_data
30
+ puts 'Processing request. Please wait...'.yellow if display?
31
+ data = {}
32
+ sum_total_gain_xp = 0
33
+
34
+ MONTHLY_XP_SKILL_ID_CONST.each do |skill_id, skill_name|
35
+ puts "Collecting #{skill_name} data...".yellow if display?
36
+ parsed_response = monthly_xp_request(skill_id)
37
+
38
+ data[skill_name] = parsed_response
39
+ sum_total_gain_xp += data[skill_name]['totalGain']
40
+ end
41
+
42
+ raise MissingPlayerData, 'Player data is empty.' if sum_total_gain_xp.zero?
43
+
44
+ data
45
+ end
46
+
47
+ def monthly_xp_request(skill_id)
48
+ response = RsRequest.new(url, params(skill_id)).get
49
+
50
+ JSON.parse(response.body)['monthlyXpGain'].first
51
+ end
52
+
53
+ def params(skill_id)
54
+ { searchName: player, skillid: skill_id }
55
+ end
56
+
57
+ def url
58
+ Settings.runescape_urls.monthly_xp_url
59
+ end
60
+ end
61
+ end
62
+
63
+ # Example:
64
+ # a = RsApi::MonthlyXp.new('tibthedragon')
65
+ # pp a.raw_data
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Uncomment below to run Examples in file. Continue for other related files
4
+ # require_relative '../helpers/skill_helper'
5
+ # require_relative '../helpers/player_name_helper'
6
+
7
+ module RsApi
8
+ # Base class regarding Runescape's Runemetrics API
9
+ class Runemetrics
10
+ include SkillHelper
11
+ include PlayerNameHelper
12
+
13
+ attr_reader :player
14
+
15
+ def initialize(player_name)
16
+ PlayerNameHelper.check_player_name(player_name)
17
+ @player = player_name
18
+ end
19
+
20
+ private
21
+
22
+ def display?
23
+ Settings.display_output
24
+ end
25
+
26
+ def params
27
+ raise 'implement me!'
28
+ end
29
+
30
+ def url
31
+ raise 'implement me!'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal:true
2
+
3
+ module RsApi
4
+ VERSION = '1.0.0'
5
+ end
data/lib/rs_api.rb ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal:true
2
+
3
+ require 'active_support/inflector'
4
+ require 'config'
5
+ require 'google/apis/sheets_v4'
6
+ require 'googleauth'
7
+ require 'googleauth/stores/file_token_store'
8
+ require 'json'
9
+ require 'net/http'
10
+ require 'text-table'
11
+ require 'uri'
12
+ require 'yaml'
13
+ require_relative '../config/initializers/config'
14
+ require_relative 'rs_api/patches/integer'
15
+ require_relative 'rs_api/patches/string'
16
+ require_relative 'rs_api/patches/text_table__cell_patch'
17
+ require_relative 'rs_api/patches/text_table__table_patch'
18
+
19
+ # Base RsApi module for autoloading files
20
+ module RsApi
21
+ FOLDERS_TO_LOAD = [:helpers, :hiscores, :runemetrics, ''].freeze
22
+
23
+ def self.autoload_files
24
+ FOLDERS_TO_LOAD.each do |s|
25
+ Dir[File.join(__dir__, 'rs_api', s.to_s, '*.rb')].each do |file|
26
+ class_name = File.basename(file, '.rb').camelcase
27
+ autoload class_name, file
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ RsApi.autoload_files
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rs_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Drew Spelman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-07 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Code for using player data from Runescape
14
+ email:
15
+ - drew.spelman@gmail.com
16
+ - thisfoxcodes@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - config/initializers/config.rb
22
+ - lib/rs_api.rb
23
+ - lib/rs_api/helpers/player_name_helper.rb
24
+ - lib/rs_api/helpers/skill_helper.rb
25
+ - lib/rs_api/hiscores/hiscore.rb
26
+ - lib/rs_api/hiscores/player_compare.rb
27
+ - lib/rs_api/hiscores/player_experience.rb
28
+ - lib/rs_api/patches/integer.rb
29
+ - lib/rs_api/patches/string.rb
30
+ - lib/rs_api/patches/text_table__cell_patch.rb
31
+ - lib/rs_api/patches/text_table__table_patch.rb
32
+ - lib/rs_api/rs_request.rb
33
+ - lib/rs_api/runemetrics/monthly_xp.rb
34
+ - lib/rs_api/runemetrics/runemetrics.rb
35
+ - lib/rs_api/version.rb
36
+ homepage: https://github.com/dmspelma/rs_api
37
+ licenses:
38
+ - MIT
39
+ metadata:
40
+ github_repo: ssh://github.com/dmspelma/rs_api
41
+ rubygems_mfa_required: 'false'
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - '='
49
+ - !ruby/object:Gem::Version
50
+ version: 3.1.1.18
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubygems_version: 3.3.7
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: ''
61
+ test_files: []