advent_of_ruby 0.3.5 → 0.4.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 +4 -4
- data/data/solutions/github/ZogStriP/2024/01_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/01_2.yml +7 -0
- data/data/solutions/github/ZogStriP/2024/02_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/02_2.yml +9 -0
- data/data/solutions/github/ZogStriP/2024/03_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/03_2.yml +7 -0
- data/data/solutions/github/ZogStriP/2024/04_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/04_2.yml +15 -0
- data/data/solutions/github/ZogStriP/2024/05_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/05_2.yml +16 -0
- data/data/solutions/github/ZogStriP/2024/06_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/06_2.yml +38 -0
- data/data/solutions/github/ZogStriP/2024/07_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/07_2.yml +9 -0
- data/data/solutions/github/ZogStriP/2024/08_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/08_2.yml +27 -0
- data/data/solutions/github/ZogStriP/2024/09_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/09_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/10_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/10_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/11_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/11_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/12_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/12_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/13_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/13_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/14_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/14_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/15_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/15_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/16_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/16_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/17_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/17_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/18_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/18_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/19_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/19_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/20_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/20_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/21_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/21_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/22_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/22_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/23_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/23_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/24_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/24_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2024/25_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/01_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/01_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/02_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/02_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/03_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/03_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/04_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/04_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/05_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/05_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/06_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/06_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/07_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/07_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/08_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/08_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/09_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/09_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/10_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/10_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/11_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/11_2.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/12_1.yml +1 -0
- data/data/solutions/github/ZogStriP/2025/12_2.yml +1 -0
- data/data/solutions/github/ahorner/2025/01_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/01_2.yml +37 -0
- data/data/solutions/github/ahorner/2025/02_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/02_2.yml +41 -0
- data/data/solutions/github/ahorner/2025/03_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/03_2.yml +24 -0
- data/data/solutions/github/ahorner/2025/04_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/04_2.yml +39 -0
- data/data/solutions/github/ahorner/2025/05_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/05_2.yml +48 -0
- data/data/solutions/github/ahorner/2025/06_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/06_2.yml +44 -0
- data/data/solutions/github/ahorner/2025/07_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/07_2.yml +56 -0
- data/data/solutions/github/ahorner/2025/08_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/08_2.yml +59 -0
- data/data/solutions/github/ahorner/2025/09_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/09_2.yml +101 -0
- data/data/solutions/github/ahorner/2025/10_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/10_2.yml +72 -0
- data/data/solutions/github/ahorner/2025/11_1.yml +1 -0
- data/data/solutions/github/ahorner/2025/11_2.yml +45 -0
- data/data/solutions/github/ahorner/2025/12_1.yml +1 -0
- data/data/solutions/github/eregon/2025/01_1.yml +17 -0
- data/data/solutions/github/eregon/2025/01_2.yml +14 -0
- data/data/solutions/github/eregon/2025/02_1.yml +22 -0
- data/data/solutions/github/eregon/2025/02_2.yml +58 -0
- data/data/solutions/github/eregon/2025/03_1.yml +13 -0
- data/data/solutions/github/eregon/2025/03_2.yml +13 -0
- data/data/solutions/github/eregon/2025/04_1.yml +14 -0
- data/data/solutions/github/eregon/2025/04_2.yml +16 -0
- data/data/solutions/github/eregon/2025/05_1.yml +1 -0
- data/data/solutions/github/eregon/2025/05_2.yml +1 -0
- data/data/solutions/github/eregon/2025/06_1.yml +1 -0
- data/data/solutions/github/eregon/2025/06_2.yml +1 -0
- data/data/solutions/github/eregon/2025/07_1.yml +1 -0
- data/data/solutions/github/eregon/2025/07_2.yml +1 -0
- data/data/solutions/github/eregon/2025/08_1.yml +1 -0
- data/data/solutions/github/eregon/2025/08_2.yml +1 -0
- data/data/solutions/github/eregon/2025/09_1.yml +1 -0
- data/data/solutions/github/eregon/2025/09_2.yml +1 -0
- data/data/solutions/github/eregon/2025/10_1.yml +1 -0
- data/data/solutions/github/eregon/2025/10_2.yml +1 -0
- data/data/solutions/github/eregon/2025/11_1.yml +1 -0
- data/data/solutions/github/eregon/2025/11_2.yml +1 -0
- data/data/solutions/github/eregon/2025/12_1.yml +1 -0
- data/data/solutions/github/erikw/2025/01_1.yml +1 -0
- data/data/solutions/github/erikw/2025/01_2.yml +1 -0
- data/data/solutions/github/erikw/2025/02_1.yml +1 -0
- data/data/solutions/github/erikw/2025/02_2.yml +1 -0
- data/data/solutions/github/erikw/2025/03_1.yml +1 -0
- data/data/solutions/github/erikw/2025/03_2.yml +1 -0
- data/data/solutions/github/erikw/2025/04_1.yml +1 -0
- data/data/solutions/github/erikw/2025/04_2.yml +1 -0
- data/data/solutions/github/erikw/2025/05_1.yml +1 -0
- data/data/solutions/github/erikw/2025/05_2.yml +1 -0
- data/data/solutions/github/erikw/2025/06_1.yml +1 -0
- data/data/solutions/github/erikw/2025/06_2.yml +1 -0
- data/data/solutions/github/erikw/2025/07_1.yml +1 -0
- data/data/solutions/github/erikw/2025/07_2.yml +1 -0
- data/data/solutions/github/erikw/2025/08_1.yml +1 -0
- data/data/solutions/github/erikw/2025/08_2.yml +1 -0
- data/data/solutions/github/erikw/2025/09_1.yml +1 -0
- data/data/solutions/github/erikw/2025/09_2.yml +1 -0
- data/data/solutions/github/erikw/2025/10_1.yml +1 -0
- data/data/solutions/github/erikw/2025/10_2.yml +1 -0
- data/data/solutions/github/erikw/2025/11_1.yml +1 -0
- data/data/solutions/github/erikw/2025/11_2.yml +1 -0
- data/data/solutions/github/erikw/2025/12_1.yml +1 -0
- data/data/solutions/github/erikw/2025/12_2.yml +1 -0
- data/data/solutions/github/gchan/2025/01_1.yml +25 -0
- data/data/solutions/github/gchan/2025/01_2.yml +32 -0
- data/data/solutions/github/gchan/2025/02_1.yml +18 -0
- data/data/solutions/github/gchan/2025/02_2.yml +21 -0
- data/data/solutions/github/gchan/2025/03_1.yml +15 -0
- data/data/solutions/github/gchan/2025/03_2.yml +23 -0
- data/data/solutions/github/gchan/2025/04_1.yml +27 -0
- data/data/solutions/github/gchan/2025/04_2.yml +40 -0
- data/data/solutions/github/gchan/2025/05_1.yml +20 -0
- data/data/solutions/github/gchan/2025/05_2.yml +36 -0
- data/data/solutions/github/gchan/2025/06_1.yml +11 -0
- data/data/solutions/github/gchan/2025/06_2.yml +28 -0
- data/data/solutions/github/gchan/2025/07_1.yml +33 -0
- data/data/solutions/github/gchan/2025/07_2.yml +29 -0
- data/data/solutions/github/gchan/2025/08_1.yml +48 -0
- data/data/solutions/github/gchan/2025/08_2.yml +48 -0
- data/data/solutions/github/gchan/2025/09_1.yml +16 -0
- data/data/solutions/github/gchan/2025/09_2.yml +60 -0
- data/data/solutions/github/gchan/2025/10_1.yml +49 -0
- data/data/solutions/github/gchan/2025/10_2.yml +154 -0
- data/data/solutions/github/gchan/2025/11_1.yml +43 -0
- data/data/solutions/github/gchan/2025/11_2.yml +33 -0
- data/data/solutions/github/gchan/2025/12_1.yml +51 -0
- data/data/solutions/reddit/ruby/2017/06.yml +0 -2
- data/data/solutions/reddit/ruby/2020/02.yml +0 -1
- data/data/solutions/reddit/ruby/2024/02.yml +0 -1
- data/data/solutions/reddit/ruby/2025/01.yml +187 -0
- data/data/solutions/reddit/ruby/2025/02.yml +185 -0
- data/data/solutions/reddit/ruby/2025/03.yml +369 -0
- data/data/solutions/reddit/ruby/2025/04.yml +217 -0
- data/data/solutions/reddit/ruby/2025/05.yml +324 -0
- data/data/solutions/reddit/ruby/2025/06.yml +246 -0
- data/data/solutions/reddit/ruby/2025/07.yml +213 -0
- data/data/solutions/reddit/ruby/2025/08.yml +73 -0
- data/data/solutions/reddit/ruby/2025/09.yml +26 -0
- data/data/solutions/reddit/ruby/2025/10.yml +73 -0
- data/data/solutions/reddit/ruby/2025/11.yml +69 -0
- data/data/solutions/reddit/ruby/2025/12.yml +1 -0
- data/lib/arb/arb.rb +0 -5
- data/lib/arb/cli/bootstrap.rb +1 -1
- data/lib/arb/cli/commit.rb +1 -1
- data/lib/arb/cli/progress.rb +4 -8
- data/lib/arb/cli/run.rb +1 -1
- data/lib/arb/cli/shared/git.rb +3 -4
- data/lib/arb/cli/shared/year_day_validator.rb +3 -8
- data/lib/arb/files/spec.rb +2 -2
- data/lib/arb/util.rb +58 -0
- data/lib/arb/version.rb +1 -1
- metadata +184 -23
- data/lib/download_solutions/api/github/repos.rb +0 -54
- data/lib/download_solutions/api/github.rb +0 -164
- data/lib/download_solutions/api/reddit/add_missing_replies.rb +0 -43
- data/lib/download_solutions/api/reddit/clean_bodies.rb +0 -64
- data/lib/download_solutions/api/reddit/filter_by_language.rb +0 -32
- data/lib/download_solutions/api/reddit/get_initial_response.rb +0 -30
- data/lib/download_solutions/api/reddit/get_serial_comments.rb +0 -145
- data/lib/download_solutions/api/reddit/megathread_ids.rb +0 -19
- data/lib/download_solutions/api/reddit/params.rb +0 -40
- data/lib/download_solutions/api/reddit/reject_unwanted_replies.rb +0 -31
- data/lib/download_solutions/api/reddit/remove_ids.rb +0 -26
- data/lib/download_solutions/api/reddit/remove_language_tags.rb +0 -29
- data/lib/download_solutions/api/reddit.rb +0 -101
- data/lib/download_solutions/cli/cli/shared.rb +0 -35
- data/lib/download_solutions/cli/github.rb +0 -107
- data/lib/download_solutions/cli/reddit.rb +0 -64
- data/lib/download_solutions/download_solutions.rb +0 -18
- data/lib/download_solutions/reverse_markdown/converters/br.rb +0 -15
- data/lib/download_solutions/reverse_markdown/converters/pre.rb +0 -46
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Api
|
|
3
|
-
class Reddit
|
|
4
|
-
class GetSerialComments
|
|
5
|
-
MAX_SLEEP_COUNT = 10
|
|
6
|
-
|
|
7
|
-
# Equivalent to repeatedly pressing "View more comments" in a thread's top
|
|
8
|
-
# level (or the "+" below a comment) until reaching the end. "Serial"
|
|
9
|
-
# because this doesn't fetch all replies; many (not all) replies are in
|
|
10
|
-
# "more children" nodes and not yet fetched.
|
|
11
|
-
#
|
|
12
|
-
# @param params [Reddit::Params]
|
|
13
|
-
# @param parent_id [String] the ID of the parent, by default the thread.
|
|
14
|
-
# @return [Array(Array<Hash>, Array<Hash>)] comments and "more children" nodes.
|
|
15
|
-
#
|
|
16
|
-
# @raise [MaxSleepCountReachedError] if Reddit seemingly throttled for an
|
|
17
|
-
# unusually long time, "seemingly" because the only sign is an empty
|
|
18
|
-
# JSON response body after loading additional comments.
|
|
19
|
-
# @raise [MultipleMoreChildrensError] if there are multiple "more children"
|
|
20
|
-
# nodes for the thread or a comment; only one at a time is expected.
|
|
21
|
-
# If there are ever more, this algorithm will need to change from a
|
|
22
|
-
# serial loop to recursion.
|
|
23
|
-
def self.call(params:, parent_id: params.thread_id)
|
|
24
|
-
comments = initial_comments_or_replies(params, parent_id) || (return [[], []])
|
|
25
|
-
|
|
26
|
-
loop do
|
|
27
|
-
more_top_level_childrens, comments = comments.partition {
|
|
28
|
-
it[:children] && it[:parent_id] == parent_id
|
|
29
|
-
}
|
|
30
|
-
break unless more_top_level_childrens.any?
|
|
31
|
-
raise Reddit::MultipleMoreChildrensError if more_top_level_childrens.count > 1
|
|
32
|
-
|
|
33
|
-
# Loop again to fetch more if there are more top-level comments.
|
|
34
|
-
comments += fetch_more_children(params, more_top_level_childrens.first, parent_id)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
more_childrens, comments = comments.partition { it[:children] }
|
|
38
|
-
|
|
39
|
-
[comments, more_childrens]
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
private_class_method def self.initial_comments_or_replies(params, parent_id)
|
|
43
|
-
if parent_id == params.thread_id
|
|
44
|
-
parse_initial_response(params)
|
|
45
|
-
else
|
|
46
|
-
more_childrens = params.more_childrens.select {
|
|
47
|
-
it[:parent_id] == parent_id
|
|
48
|
-
}
|
|
49
|
-
raise Reddit::MultipleMoreChildrensError if more_childrens.count > 1
|
|
50
|
-
|
|
51
|
-
if more_childrens.empty?
|
|
52
|
-
# signal to return empty arrays for comments and more_childrens
|
|
53
|
-
return nil
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
fetch_more_children(params, more_childrens.first, parent_id)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
private_class_method def self.parse_initial_response(params)
|
|
61
|
-
simplify_comments(
|
|
62
|
-
JSON.parse(params.initial_response.body).dig(-1, "data", "children")
|
|
63
|
-
)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
private_class_method def self.fetch_more_children(params, more_children, parent_id)
|
|
67
|
-
response = nil
|
|
68
|
-
sleep_count = 0
|
|
69
|
-
loop do
|
|
70
|
-
# POST because a GET request would sometimes be too long.
|
|
71
|
-
response = params.connection.post(
|
|
72
|
-
"/api/morechildren.json",
|
|
73
|
-
"link_id=#{params.thread_id}" \
|
|
74
|
-
"&children=#{more_children[:children].join(",")}"
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
if response.body.empty?
|
|
78
|
-
if sleep_count < MAX_SLEEP_COUNT
|
|
79
|
-
puts PASTEL.bright_black("Throttled by Reddit. Sleeping for 60 seconds...")
|
|
80
|
-
sleep_count += 1
|
|
81
|
-
sleep 60
|
|
82
|
-
else
|
|
83
|
-
raise Reddit::MaxSleepCountReachedError
|
|
84
|
-
end
|
|
85
|
-
else
|
|
86
|
-
puts "Continuing to fetch comments..." if sleep_count > 1
|
|
87
|
-
break
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
simplify_comments(
|
|
92
|
-
JSON.parse(response.body).dig("jquery", 10, 3, 0)
|
|
93
|
-
)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
private_class_method def self.simplify_comments(raw_comments)
|
|
97
|
-
raw_more_childrens, raw_comments = raw_comments.partition { it["kind"] == "more" }
|
|
98
|
-
|
|
99
|
-
comments = raw_comments.map { |raw_comment|
|
|
100
|
-
simplify_comment(raw_comment, raw_more_childrens)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
more_childrens = raw_more_childrens.filter_map {
|
|
104
|
-
if it["data"]["children"].any?
|
|
105
|
-
{
|
|
106
|
-
children: it["data"]["children"],
|
|
107
|
-
parent_id: it["data"]["parent_id"]
|
|
108
|
-
}
|
|
109
|
-
end
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
comments + more_childrens
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
private_class_method def self.simplify_comment(raw_comment, raw_more_childrens)
|
|
116
|
-
{
|
|
117
|
-
author: raw_comment["data"]["author"],
|
|
118
|
-
url: "https://www.reddit.com#{raw_comment["data"]["permalink"]}".delete_suffix("/"),
|
|
119
|
-
body: raw_comment["data"]["body"],
|
|
120
|
-
id: raw_comment["data"]["name"],
|
|
121
|
-
parent_id: raw_comment["data"]["parent_id"],
|
|
122
|
-
replies: simplify_replies(raw_comment, raw_more_childrens)
|
|
123
|
-
}
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
private_class_method def self.simplify_replies(raw_comment, raw_more_childrens)
|
|
127
|
-
return [] if raw_comment["data"]["replies"].nil? || raw_comment["data"]["replies"].empty?
|
|
128
|
-
|
|
129
|
-
raw_comment.dig("data", "replies", "data", "children").filter_map { |child|
|
|
130
|
-
if child["kind"] == "more"
|
|
131
|
-
# Move "more children" nodes (listing additional replies that are
|
|
132
|
-
# not yet fetched) out to an array that is appended onto the
|
|
133
|
-
# upper-level comments (see #simplify_comments), so that they can
|
|
134
|
-
# all be dealt with together by #add_missing_replies!
|
|
135
|
-
raw_more_childrens << child
|
|
136
|
-
next nil
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
simplify_comment(child, raw_more_childrens)
|
|
140
|
-
}
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Api
|
|
3
|
-
class Reddit
|
|
4
|
-
# From https://www.reddit.com/r/adventofcode/wiki/archives/solution_megathreads
|
|
5
|
-
MEGATHREAD_IDS = {
|
|
6
|
-
2024 => %w[1h3vp6n 1h4ncyr 1h5frsp 1h689qf 1h71eyz 1h7tovg 1h8l3z5 1h9bdmp 1ha27bo 1hau6hl 1hbm0al 1hcdnk0 1hd4wda 1hdvhvu 1hele8m 1hfboft 1hg38ah 1hguacy 1hhlb8g 1hicdtb 1hj2odw 1hjroap 1hkgj5b 1hl698z 1hlu4ht],
|
|
7
|
-
2023 => %w[1883ibu 188w447 189m3qw 18actmy 18b4b0r 18bwe6t 18cnzbm 18df7px 18e5ytd 18evyu9 18fmrjk 18ge41g 18h940b 18i0xtn 18isayp 18jjpfk 18k9ne5 18l0qtr 18ltr8m 18mmfxb 18nevo3 18o7014 18oy4pc 18pnycy 18qbsxs],
|
|
8
|
-
2022 => %w[z9ezjb zac2v2 zb865p zc0zta zcxid5 zdw0u6 zesk40 zfpnka zgnice zhjfo4 zifqmh zjnruc zkmyh4 zli1rd zmcn64 zn6k1l znykq2 zoqhvy zpihwi zqezkn zrav4h zsct8w zt6xz5 zu28ij zur1an],
|
|
9
|
-
2021 => %w[r66vow r6zd93 r7r0ff r8i1lq r9824c r9z49j rar7ty rbj87a rca6vp rd0s54 rds32p rehj2r rf7onx rfzq6f rgqzt5 rhj2hm ri9kdq rizw2c rjpf7f rkf5ek rl6p8y rlxhmg rmnozs rnejv5 ro2uav],
|
|
10
|
-
2020 => %w[k4e4lm k52psu k5qsrk k6e8sw k71h6r k7ndux k8a31f k8xw8h k9lfwj ka8z8x kaw6oz kbj5me kc4njx kcr1ct kdf85p ke2qp6 keqsfa kfeldk kg1mro kgo01p khaiyk khyjgv kimluc kj96iw kjtg7y],
|
|
11
|
-
2019 => %w[e4axxe e4u0rw e5bz2w e5u5fv e6carb e6tyva e7a4nj e7pkmt e85b6d e8m1z3 e92jm2 e9j0ve e9zgse eafj32 eaurfo ebai4g ebr7dg ec8090 ecogl3 ed5ei2 edll5a ee0rqi eefva8 eewjtt efca4m],
|
|
12
|
-
2018 => %w[a20646 a2aimr a2lesz a2xef8 a3912m a3kr4r a3wmnl a47ubw a4i97s a4skra a53r6i a5eztl a5qd71 a61ojp a6chwa a6mf8a a6wpup a77xq6 a7j9zc a7uk3f a86jgt a8i1cy a8s17l a91ysq a9c61w],
|
|
13
|
-
2017 => %w[7gsrc2 7h0rnm 7h7ufl 7hf5xb 7hngbn 7hvtoq 7i44pg 7icnff 7iksqc 7irzg5 7izym2 7j89tr 7jgyrt 7jpelc 7jxkiw 7k572l 7kc0xw 7kj35s 7kr2ac 7kz6ik 7l78eb 7lf943 7lms6p 7lte5z 7lzo3l],
|
|
14
|
-
2016 => %w[5fur6q 5g1hfm 5g80ck 5gdvve 5gk2yv 5gr0xf 5gy1f2 5h52ro 5hbygy 5hijk5 5hoia9 5hus40 5i1q0h 5i8pzz 5ifn4v 5imh3d 5isvxv 5iyp50 5j4lp1 5jbeqo 5ji29h 5jor9q 5jvbzt 5k1he1 5k6yfu],
|
|
15
|
-
2015 => %w[3uyl7s 3v3w2f 3v8roh 3vdn8a 3viazx 3vmltn 3vr4m4 3vw32y 3w192e 3w6h3m 3wbzyv 3wh73d 3wm0oy 3wqtx2 3wwj84 3x1i26 3x6cyr 3xb3cj 3xflz8 3xjpp2 3xnyoi 3xspyl 3xxdxt 3y1s7f 3y5jco]
|
|
16
|
-
}
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Api
|
|
3
|
-
class Reddit
|
|
4
|
-
# A parameter object that is passed into each step within Reddit#get_comments.
|
|
5
|
-
class Params
|
|
6
|
-
attr_reader :year, :day, :languages, :connection, :megathread_path, :thread_id
|
|
7
|
-
attr_accessor :initial_response, :original_comments, :more_childrens, :comments
|
|
8
|
-
|
|
9
|
-
# @param year [Integer]
|
|
10
|
-
# @param day [Integer]
|
|
11
|
-
# @param languages [Array<String>] e.g. ["ruby"]
|
|
12
|
-
def initialize(year:, day:, languages:, connection:)
|
|
13
|
-
@year = year
|
|
14
|
-
@day = day
|
|
15
|
-
@languages = languages
|
|
16
|
-
@connection = connection
|
|
17
|
-
@megathread_path = build_megathread_path(year:, day:)
|
|
18
|
-
@thread_id = "t3_#{megathread_id(year:, day:)}"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def build_megathread_path(year:, day:)
|
|
24
|
-
if year == 2015 && day == 1
|
|
25
|
-
return "/r/programming/comments/#{Reddit::MEGATHREAD_IDS[2015][0]}/daily_programming_puzzles_at_advent_of_code.json"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
slug = "day_#{day.to_i}_solutions"
|
|
29
|
-
slug = "#{year.to_i}_#{slug}" if year > 2015
|
|
30
|
-
|
|
31
|
-
"/r/adventofcode/comments/#{megathread_id(year:, day:)}/#{slug}.json"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def megathread_id(year:, day:)
|
|
35
|
-
Reddit::MEGATHREAD_IDS[year.to_i][day - 1]
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Api
|
|
3
|
-
class Reddit
|
|
4
|
-
class RejectUnwantedReplies
|
|
5
|
-
# Filters out replies that are not gennerally relevant for posterity:
|
|
6
|
-
# - removed/deleted with no replies
|
|
7
|
-
# - by moderators
|
|
8
|
-
# - by bots
|
|
9
|
-
#
|
|
10
|
-
# @param params [Reddit::Params]
|
|
11
|
-
# @return [void] params.comments is modified in place.
|
|
12
|
-
def self.call(params:)
|
|
13
|
-
params.comments.each do |comment|
|
|
14
|
-
reject_unwanted_replies!(comment)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
private_class_method def self.reject_unwanted_replies!(comment)
|
|
19
|
-
comment[:replies].each do |reply|
|
|
20
|
-
reject_unwanted_replies!(reply)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
comment[:replies].reject! do
|
|
24
|
-
(["[removed]", "[deleted]"].include?(it[:body].strip) && it[:replies].empty?) ||
|
|
25
|
-
%w[AutoModerator daggerdragon backtickbot].include?(it[:author])
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Api
|
|
3
|
-
class Reddit
|
|
4
|
-
class RemoveIds
|
|
5
|
-
# Removes IDs from comments.
|
|
6
|
-
#
|
|
7
|
-
# @param params [Reddit::Params]
|
|
8
|
-
# @return [void] params.comments is modified in place.
|
|
9
|
-
def self.call(params:)
|
|
10
|
-
params.comments.each do |comment|
|
|
11
|
-
remove_ids!(comment)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
private_class_method def self.remove_ids!(comment)
|
|
16
|
-
comment.delete(:id)
|
|
17
|
-
comment.delete(:parent_id)
|
|
18
|
-
|
|
19
|
-
comment[:replies].each do |reply|
|
|
20
|
-
remove_ids!(reply)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Api
|
|
3
|
-
class Reddit
|
|
4
|
-
class RemoveLanguageTags
|
|
5
|
-
# Removes language tags from comment bodies.
|
|
6
|
-
#
|
|
7
|
-
# @param params [Reddit::Params]
|
|
8
|
-
# @return [void] params.comments is modified in place.
|
|
9
|
-
def self.call(params:)
|
|
10
|
-
params.comments.each do |comment|
|
|
11
|
-
remove_language_tag!(comment, params.languages)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
private_class_method def self.remove_language_tag!(comment, languages)
|
|
16
|
-
languages.each do |language|
|
|
17
|
-
comment[:body].sub!(/\[[[:punct:]]*language:\s*#{language}[[:punct:]]*\]/i, "")
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
comment[:body].strip!
|
|
21
|
-
|
|
22
|
-
comment[:replies].each do |reply|
|
|
23
|
-
remove_language_tag!(reply, languages)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Api
|
|
3
|
-
class Reddit
|
|
4
|
-
MaxSleepCountReachedError = Class.new(StandardError)
|
|
5
|
-
MultipleMoreChildrensError = Class.new(StandardError)
|
|
6
|
-
|
|
7
|
-
private attr_reader :user_agent, :client_id, :client_secret, :username, :password
|
|
8
|
-
|
|
9
|
-
# @param client_id [String]
|
|
10
|
-
# @param client_secret [String]
|
|
11
|
-
# @param username [String]
|
|
12
|
-
# @param password [String]
|
|
13
|
-
def initialize(client_id:, client_secret:, username:, password:)
|
|
14
|
-
@user_agent = "AdventOfRubyScript/#{Arb::VERSION} by fpsvogel"
|
|
15
|
-
@client_id = client_id
|
|
16
|
-
@client_secret = client_secret
|
|
17
|
-
@username = username
|
|
18
|
-
@password = password
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @param year [Integer]
|
|
22
|
-
# @param day [Integer]
|
|
23
|
-
# @param languages [Array<String>] e.g. ["ruby"]
|
|
24
|
-
# @return [Array<Hash>]
|
|
25
|
-
#
|
|
26
|
-
# @raise [MaxSleepCountReachedError] if Reddit seemingly throttled for an
|
|
27
|
-
# unusually long time, "seemingly" because the only sign is an empty
|
|
28
|
-
# JSON response body after loading additional comments.
|
|
29
|
-
# @raise [MultipleMoreChildrensError] if there are multiple "more children"
|
|
30
|
-
# nodes for the thread or a comment; only one at a time is expected.
|
|
31
|
-
def get_comments(year:, day:, languages:)
|
|
32
|
-
params = Params.new(
|
|
33
|
-
year:,
|
|
34
|
-
day:,
|
|
35
|
-
languages:,
|
|
36
|
-
connection:
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
initial_response = GetInitialResponse.call(params:)
|
|
40
|
-
params.initial_response = initial_response
|
|
41
|
-
|
|
42
|
-
# Keep unfetched replies ("more children" nodes) separate so that after
|
|
43
|
-
# filtering, the replies to filtered-in comments can then be fetched.
|
|
44
|
-
original_comments, more_childrens = GetSerialComments.call(params:)
|
|
45
|
-
params.original_comments = original_comments
|
|
46
|
-
params.more_childrens = more_childrens
|
|
47
|
-
|
|
48
|
-
filtered_comments = FilterByLanguage.call(params:)
|
|
49
|
-
params.comments = filtered_comments
|
|
50
|
-
|
|
51
|
-
# These operations modify params#comments in place.
|
|
52
|
-
AddMissingReplies.call(params:)
|
|
53
|
-
RejectUnwantedReplies.call(params:)
|
|
54
|
-
CleanBodies.call(params:)
|
|
55
|
-
RemoveLanguageTags.call(params:)
|
|
56
|
-
RemoveIds.call(params:)
|
|
57
|
-
|
|
58
|
-
params.comments
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
private
|
|
62
|
-
|
|
63
|
-
def connection
|
|
64
|
-
@connection ||= Faraday.new(
|
|
65
|
-
url: "https://oauth.reddit.com",
|
|
66
|
-
headers: {
|
|
67
|
-
"User-Agent" => user_agent,
|
|
68
|
-
"Accept" => "application/json"
|
|
69
|
-
}
|
|
70
|
-
) do |f|
|
|
71
|
-
f.request :authorization, "Bearer", -> { auth_token }
|
|
72
|
-
f.request :retry, {
|
|
73
|
-
max: 5,
|
|
74
|
-
interval: 0.5,
|
|
75
|
-
interval_randomness: 0.5,
|
|
76
|
-
backoff_factor: 2
|
|
77
|
-
}
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def auth_token
|
|
82
|
-
connection = Faraday.new(
|
|
83
|
-
url: "https://www.reddit.com",
|
|
84
|
-
headers: {
|
|
85
|
-
"User-Agent" => user_agent
|
|
86
|
-
}
|
|
87
|
-
) do |f|
|
|
88
|
-
f.request :authorization, :basic, client_id, client_secret
|
|
89
|
-
f.response :json
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
response = connection.post(
|
|
93
|
-
"/api/v1/access_token",
|
|
94
|
-
"grant_type=password&username=#{username}&password=#{password}"
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
response.body["access_token"]
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Cli
|
|
3
|
-
private_class_method def self.validate_year_and_day(year:, day:)
|
|
4
|
-
if day
|
|
5
|
-
if year.nil?
|
|
6
|
-
raise InputError, "Year must be specified when day is specified."
|
|
7
|
-
end
|
|
8
|
-
if !day.between?(1, 25) && Date.new(year, 12, day) > Date.today
|
|
9
|
-
raise InputError, "Day must be between 1 and 25, and <= today."
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
if year && !year.between?(2015, Date.today.year)
|
|
13
|
-
raise InputError, "Year must be between 2015 and this year."
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
private_class_method def self.max_year_and_day(year:, day:)
|
|
18
|
-
if Date.today.year == year && Date.today.month == 12
|
|
19
|
-
[Date.today.year, Date.today.day]
|
|
20
|
-
else
|
|
21
|
-
[Date.today.year - 1, 25]
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
private_class_method def self.output_initial_message(source:, year:, day:, force:, detail:)
|
|
26
|
-
force_description = PASTEL.red("FORCE ") if force
|
|
27
|
-
year_description = year.nil? ? "all years" : year.to_s
|
|
28
|
-
day_description = day.nil? ? "" : "##{day.to_s.rjust(2, "0")}"
|
|
29
|
-
time_description = PASTEL.blue("#{year_description}#{day_description}")
|
|
30
|
-
|
|
31
|
-
puts "#{force_description}Downloading #{source} solutions from #{time_description} #{detail}..."
|
|
32
|
-
puts
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Cli
|
|
3
|
-
def self.github(year: nil, day: nil, author: nil, force: false)
|
|
4
|
-
validate_year_and_day(year:, day:)
|
|
5
|
-
|
|
6
|
-
author_name = author.nil? ? "all authors" : PASTEL.blue(author)
|
|
7
|
-
detail = "by #{author_name}"
|
|
8
|
-
output_initial_message(source: "GitHub", year:, day:, force:, detail:)
|
|
9
|
-
|
|
10
|
-
github_directory = File.join("data", "solutions", "github")
|
|
11
|
-
|
|
12
|
-
repos = Api::Github::REPOS
|
|
13
|
-
if author
|
|
14
|
-
if repos.key?(author)
|
|
15
|
-
repos = repos.select { it == author }
|
|
16
|
-
else
|
|
17
|
-
raise InputError, "Repo author #{PASTEL.blue(author)} not found."
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
max_year, max_day = max_year_and_day(year:, day:)
|
|
22
|
-
|
|
23
|
-
authors = repos.keys
|
|
24
|
-
authors.each do |author|
|
|
25
|
-
author_directory = File.join(github_directory, author)
|
|
26
|
-
Dir.mkdir(author_directory) unless Dir.exist?(author_directory)
|
|
27
|
-
|
|
28
|
-
(year || 2015).upto(year || max_year) do |current_year|
|
|
29
|
-
year_directory = File.join(author_directory, current_year.to_s)
|
|
30
|
-
|
|
31
|
-
existing_solutions =
|
|
32
|
-
if Dir.exist?(year_directory)
|
|
33
|
-
Dir.entries(year_directory)
|
|
34
|
-
.filter_map {
|
|
35
|
-
it.delete_suffix(".yml").split("_").map(&:to_i) if it.end_with?(".yml")
|
|
36
|
-
}.sort
|
|
37
|
-
else
|
|
38
|
-
[]
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
solutions = github_api.get_solutions(
|
|
42
|
-
author:,
|
|
43
|
-
year: current_year,
|
|
44
|
-
input_day: day,
|
|
45
|
-
max_day:,
|
|
46
|
-
force:,
|
|
47
|
-
existing_solutions:
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
skipped_list = solutions_str(solutions[:skipped])
|
|
51
|
-
if solutions[:skipped].any?
|
|
52
|
-
puts "#{PASTEL.yellow.bold("Skipping")} from #{author} #{current_year}, already existing: #{PASTEL.yellow(skipped_list)}"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
not_found_list = solutions_str(solutions[:not_found])
|
|
56
|
-
if solutions[:not_found].any?
|
|
57
|
-
puts "#{PASTEL.red.bold("Not found")} from #{author} #{current_year}: #{PASTEL.red(not_found_list)}"
|
|
58
|
-
if solutions[:not_found].size < 49
|
|
59
|
-
# Save empty files for not found solutions, so that they won't be
|
|
60
|
-
# attempted to be downloaded again.
|
|
61
|
-
Dir.mkdir(year_directory) unless Dir.exist?(year_directory)
|
|
62
|
-
solutions[:not_found].each do |(day, part)|
|
|
63
|
-
path = File.join(year_directory, "#{day.to_s.rjust(2, "0")}_#{part}.yml")
|
|
64
|
-
File.write(path, [].to_yaml)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
if solutions[:new].any?
|
|
70
|
-
Dir.mkdir(year_directory) unless Dir.exist?(year_directory)
|
|
71
|
-
solutions[:new].each do |(day, part), content|
|
|
72
|
-
path = File.join(year_directory, "#{day.to_s.rjust(2, "0")}_#{part}.yml")
|
|
73
|
-
File.write(path, content.to_yaml(line_width: -1))
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
new_list = solutions_str(solutions[:new].keys)
|
|
77
|
-
puts "#{PASTEL.blue.bold("Saved")} from #{author} #{current_year}: #{PASTEL.blue(new_list)}"
|
|
78
|
-
puts "Saved to #{year_directory}"
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
puts
|
|
83
|
-
end
|
|
84
|
-
rescue InputError => e
|
|
85
|
-
puts PASTEL.red(e.message)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private_class_method def self.solutions_str(solutions)
|
|
89
|
-
if solutions.count == 49
|
|
90
|
-
"all"
|
|
91
|
-
else
|
|
92
|
-
solutions.sort.map { it.join(".") }.join(", ")
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
private_class_method def self.github_api
|
|
97
|
-
return @github_api if @github_api
|
|
98
|
-
|
|
99
|
-
github_token = "GITHUB_TOKEN"
|
|
100
|
-
|
|
101
|
-
Dotenv.load
|
|
102
|
-
Dotenv.require_keys([github_token])
|
|
103
|
-
|
|
104
|
-
@github_api = Api::Github.new(auth_token: ENV[github_token])
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
module DownloadSolutions
|
|
2
|
-
module Cli
|
|
3
|
-
def self.reddit(year: nil, day: nil, languages: ["ruby"], force: false)
|
|
4
|
-
validate_year_and_day(year:, day:)
|
|
5
|
-
|
|
6
|
-
language_names = PASTEL.blue(languages.join(", "))
|
|
7
|
-
detail = "for #{language_names}"
|
|
8
|
-
output_initial_message(source: "Reddit", year:, day:, force:, detail:)
|
|
9
|
-
|
|
10
|
-
language_directory = File.join("data", "solutions", "reddit", languages.join("-"))
|
|
11
|
-
Dir.mkdir(language_directory) unless Dir.exist?(language_directory)
|
|
12
|
-
|
|
13
|
-
max_year, max_day = max_year_and_day(year:, day:)
|
|
14
|
-
|
|
15
|
-
(year || 2015).upto(year || max_year) do |current_year|
|
|
16
|
-
year_directory = File.join("data", "solutions", "reddit", languages.join("-"), current_year.to_s)
|
|
17
|
-
Dir.mkdir(year_directory) unless Dir.exist?(year_directory)
|
|
18
|
-
|
|
19
|
-
(day || 1).upto(day || max_day) do |current_day|
|
|
20
|
-
path = File.join("data", "solutions", "reddit", languages.join("-"), year.to_s, "#{current_day.to_s.rjust(2, "0")}.yml")
|
|
21
|
-
|
|
22
|
-
if File.exist?(path) && !force
|
|
23
|
-
puts PASTEL.yellow("Skipping #{PASTEL.yellow.bold("#{current_year}##{current_day.to_s.rjust(2, "0")}")} as it already exists.")
|
|
24
|
-
puts
|
|
25
|
-
next
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
comments = reddit_api.get_comments(
|
|
29
|
-
year: current_year,
|
|
30
|
-
day: current_day,
|
|
31
|
-
languages:
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
File.write(path, comments.to_yaml(line_width: -1))
|
|
35
|
-
|
|
36
|
-
puts "Saved #{PASTEL.blue("#{current_year}##{current_day.to_s.rjust(2, "0")}")} to #{path}"
|
|
37
|
-
puts
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
rescue InputError => e
|
|
41
|
-
puts PASTEL.red(e.message)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private_class_method def self.reddit_api
|
|
45
|
-
return @reddit_api if @reddit_api
|
|
46
|
-
|
|
47
|
-
reddit_api_keys = %w[
|
|
48
|
-
REDDIT_CLIENT_ID
|
|
49
|
-
REDDIT_CLIENT_SECRET
|
|
50
|
-
REDDIT_USERNAME
|
|
51
|
-
REDDIT_PASSWORD
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
Dotenv.load
|
|
55
|
-
Dotenv.require_keys(reddit_api_keys)
|
|
56
|
-
|
|
57
|
-
reddit_api_kwargs = %i[client_id client_secret username password]
|
|
58
|
-
.zip(reddit_api_keys.map { ENV[it] })
|
|
59
|
-
.to_h
|
|
60
|
-
|
|
61
|
-
@reddit_api = Api::Reddit.new(**reddit_api_kwargs)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
require "debug"
|
|
2
|
-
require "dotenv"
|
|
3
|
-
require "faraday"
|
|
4
|
-
require "faraday/retry"
|
|
5
|
-
require "pastel"
|
|
6
|
-
require "redcarpet"
|
|
7
|
-
require "reverse_markdown"
|
|
8
|
-
require "yaml"
|
|
9
|
-
require_relative "../arb/version"
|
|
10
|
-
|
|
11
|
-
module DownloadSolutions
|
|
12
|
-
InputError = Class.new(StandardError)
|
|
13
|
-
PASTEL = Pastel.new
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
Dir[File.join(__dir__, "**", "*.rb")].each do |file|
|
|
17
|
-
require file
|
|
18
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Monkeypatch. Reason: " \n" is rendered as a line break by Markdown viewers,
|
|
2
|
-
# but trailing spaces are annoying and I strip them out because that's what my
|
|
3
|
-
# editor does on save anyway. Which leaves only the newline, which is rendered
|
|
4
|
-
# as a space. So it's preferable for <br> to become a paragraph break.
|
|
5
|
-
module ReverseMarkdown
|
|
6
|
-
module Converters
|
|
7
|
-
class Br < Base
|
|
8
|
-
def convert(node, state = {})
|
|
9
|
-
"\n\n" # CHANGED from " \n"
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
register :br, Br.new
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# Monkeypatch. Reason: leading spaces are significant for aligning lines of
|
|
2
|
-
# code that are indented as well as wrapped in a fenced code block.
|
|
3
|
-
module ReverseMarkdown
|
|
4
|
-
module Converters
|
|
5
|
-
class Pre < Base
|
|
6
|
-
def convert(node, state = {})
|
|
7
|
-
content = treat_children(node, state)
|
|
8
|
-
if ReverseMarkdown.config.github_flavored
|
|
9
|
-
# CHANGED from content.strip
|
|
10
|
-
"\n```#{language(node)}\n" << content.chomp << "\n```\n"
|
|
11
|
-
else
|
|
12
|
-
"\n\n " << content.lines.to_a.join(" ") << "\n\n"
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
# Override #treat as proposed in https://github.com/xijo/reverse_markdown/pull/69
|
|
19
|
-
def treat(node, state)
|
|
20
|
-
case node.name
|
|
21
|
-
when "code", "text"
|
|
22
|
-
node.text.chomp # CHANGED from node.text.strip
|
|
23
|
-
when "br"
|
|
24
|
-
"\n"
|
|
25
|
-
else
|
|
26
|
-
super
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def language(node)
|
|
31
|
-
lang = language_from_highlight_class(node)
|
|
32
|
-
lang || language_from_confluence_class(node)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def language_from_highlight_class(node)
|
|
36
|
-
node.parent["class"].to_s[/highlight-([a-zA-Z0-9]+)/, 1]
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def language_from_confluence_class(node)
|
|
40
|
-
node["class"].to_s[/brush:\s?(:?.*);/, 1]
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
register :pre, Pre.new
|
|
45
|
-
end
|
|
46
|
-
end
|