reddit_auto 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 +7 -0
- data/lib/reddit_auto.rb +836 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 93f5030b7d722f2eb99929cf9f48838e964e78840f89dd4e32bcc9da89cc12ff
|
4
|
+
data.tar.gz: cf1b9fc23e4f52539f2086545cb6229cc216d7ea6772aa669eb033859055c730
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f4631d0b5e13b58dd863e790bfb105ad2170df36c5c366ecc983be171c98187d4117d0cb7cb79872997447be8e99d5f17801e4db9f9f08259eb30a43480bf4f2
|
7
|
+
data.tar.gz: 4b7f7ed4b8f6dcc711da055174531a2bc626c65949d9196c9e0b07f1cc6e896c164861cf901611ade348c1c204ced577fbd5265cf7de200e5e067c1a9efde674
|
data/lib/reddit_auto.rb
ADDED
@@ -0,0 +1,836 @@
|
|
1
|
+
require 'watir'
|
2
|
+
|
3
|
+
#Written by Ícaro Augusto
|
4
|
+
#Website: https://icaroaugusto.com
|
5
|
+
#Github: https://github.com/IcaroAugusto
|
6
|
+
|
7
|
+
class Reddit
|
8
|
+
PAGE_MAIN = 'https://old.reddit.com/'
|
9
|
+
PAGE_MAIN_NO_SLASH = 'https://old.reddit.com'
|
10
|
+
PAGE_MESSAGES = 'https://old.reddit.com/message/'
|
11
|
+
PAGE_SUBREDDIT = 'https://old.reddit.com/r/'
|
12
|
+
PAGE_USER = 'https://old.reddit.com/user/'
|
13
|
+
PAGE_CREATE_SUB = 'https://old.reddit.com/subreddits/create'
|
14
|
+
|
15
|
+
MESSAGE_SUBPAGES = ['inbox', 'unread', 'messages', 'comments', 'sent']
|
16
|
+
SUBREDDIT_SUBPAGES = ['hot', 'new', 'rising', 'top', 'gilded']
|
17
|
+
USER_SORTBYS = ['new', 'hot', 'top', 'controversial']
|
18
|
+
|
19
|
+
attr_accessor :browser
|
20
|
+
attr_accessor :username
|
21
|
+
|
22
|
+
def has_browser
|
23
|
+
return @browser != nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_logged_in(username)
|
27
|
+
return @browser.link(text: username).present?
|
28
|
+
end
|
29
|
+
|
30
|
+
def wait_login(username)
|
31
|
+
count = 0.0
|
32
|
+
while !is_logged_in(username)
|
33
|
+
sleep 0.25
|
34
|
+
count += 0.25
|
35
|
+
if count > 10
|
36
|
+
raise 'Reddit login failed for username: ' + username
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def login(username, password)
|
42
|
+
@username = username
|
43
|
+
@browser.goto PAGE_MAIN
|
44
|
+
@browser.text_field(name: 'user').set username
|
45
|
+
@browser.text_field(name: 'passwd').set password
|
46
|
+
@browser.checkbox(id: 'rem-login-main').set
|
47
|
+
@browser.button(text: 'login').click
|
48
|
+
wait_login(username)
|
49
|
+
end
|
50
|
+
|
51
|
+
def wait_logout
|
52
|
+
count = 0.0
|
53
|
+
while is_logged_in(@username)
|
54
|
+
sleep 0.25
|
55
|
+
count += 0.25
|
56
|
+
if count > 10
|
57
|
+
raise 'Reddit logout failed for username: ' + @username
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def logout
|
63
|
+
@browser.link(text: 'logout').click
|
64
|
+
wait_logout
|
65
|
+
end
|
66
|
+
|
67
|
+
def has_over_18
|
68
|
+
return @browser.button(name: 'over18').present?
|
69
|
+
end
|
70
|
+
|
71
|
+
def skip_over_18
|
72
|
+
@browser.button(text: 'continue').click
|
73
|
+
end
|
74
|
+
|
75
|
+
def has_over_18_new
|
76
|
+
@browser.h3(text: 'You must be 18+ to view this community').present?
|
77
|
+
end
|
78
|
+
|
79
|
+
def skip_over_18_new
|
80
|
+
@browser.link(text: 'Yes').click
|
81
|
+
end
|
82
|
+
|
83
|
+
#---------------------------------------------------------------------------------
|
84
|
+
#Messages handling
|
85
|
+
#---------------------------------------------------------------------------------
|
86
|
+
|
87
|
+
def get_message_type(div)
|
88
|
+
return div.attribute_value('data-type')
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_message_post(div)
|
92
|
+
return div.p(class: 'subject').link(class: 'title').href
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_message_author(div)
|
96
|
+
return div.attribute_value('data-author')
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_message_subreddit(div)
|
100
|
+
return div.attribute_value('data-subreddit')
|
101
|
+
end
|
102
|
+
|
103
|
+
def get_message_content(div)
|
104
|
+
return div.div(class: 'md').text
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_message_divs
|
108
|
+
all_divs = @browser.div(id: 'siteTable').divs
|
109
|
+
result = []
|
110
|
+
all_divs.each do |div|
|
111
|
+
result.push div if div.id.include? 'thing_'
|
112
|
+
end
|
113
|
+
return result
|
114
|
+
end
|
115
|
+
|
116
|
+
def is_message_voted(div, type) #type is 'up' or 'down'
|
117
|
+
return div.div(class: 'midcol').div(class: type + 'mod').present?
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_message_vote(div)
|
121
|
+
return 'up' if is_message_voted(div, 'up')
|
122
|
+
return 'down' if is_message_voted(div, 'down')
|
123
|
+
return nil
|
124
|
+
end
|
125
|
+
|
126
|
+
def vote_message(div, type) #type is 'up' or 'down'
|
127
|
+
return if is_message_voted(div, type)
|
128
|
+
div.div(class: 'midcol').div(class: type).click
|
129
|
+
end
|
130
|
+
|
131
|
+
def reply_message(div, answer)
|
132
|
+
div.li(text: 'reply').click
|
133
|
+
div.textarea.set answer
|
134
|
+
div.button(text: 'save').click
|
135
|
+
end
|
136
|
+
|
137
|
+
def get_message(div) #returns a hash with message data
|
138
|
+
result = {}
|
139
|
+
result['type'] = get_message_type(div)
|
140
|
+
result['author'] = get_message_author(div)
|
141
|
+
result['post'] = get_message_post(div) if result['type'] == 'comment'
|
142
|
+
result['subreddit'] = result['type'] == 'comment' ? get_message_subreddit(div) : result['author']
|
143
|
+
result['vote'] = get_message_vote(div) if result['type'] == 'comment'
|
144
|
+
result['content'] = get_message_content(div)
|
145
|
+
return result
|
146
|
+
end
|
147
|
+
|
148
|
+
def message_move_page(direction) #directions: next for next page, prev for previous page
|
149
|
+
button = @browser.span(class: direction + '-button')
|
150
|
+
result = button.present?
|
151
|
+
button.click if result
|
152
|
+
return result
|
153
|
+
end
|
154
|
+
|
155
|
+
def open_messages_subpage(subpage)
|
156
|
+
raise 'Unknown message subpage: ' + subpage if !MESSAGE_SUBPAGES.include? subpage
|
157
|
+
@browser.goto PAGE_MESSAGES + subpage
|
158
|
+
end
|
159
|
+
|
160
|
+
def get_messages(subpage, all_pages = false)
|
161
|
+
open_messages_subpage(subpage)
|
162
|
+
result = []
|
163
|
+
while true
|
164
|
+
get_message_divs.each do |div|
|
165
|
+
result.push get_message(div)
|
166
|
+
end
|
167
|
+
return result if !all_pages || !message_move_page('next')
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
#---------------------------------------------------------------------------------
|
172
|
+
#Submit handling
|
173
|
+
#---------------------------------------------------------------------------------
|
174
|
+
|
175
|
+
def has_submit_error
|
176
|
+
return @browser.span(text: 'you are doing that too much. try again in 9 minutes.').present?
|
177
|
+
end
|
178
|
+
|
179
|
+
def is_submit_open
|
180
|
+
return @browser.textarea(name: 'title').present? || @browser.textarea(placeholder: 'Title').present?
|
181
|
+
end
|
182
|
+
|
183
|
+
def wait_submit
|
184
|
+
count = 0.0
|
185
|
+
while is_submit_open
|
186
|
+
sleep 0.25
|
187
|
+
count += 0.25
|
188
|
+
raise 'Post submission failed!' if count >= 10
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def submit_link(subreddit, url, title)
|
193
|
+
@browser.goto PAGE_SUBREDDIT + subreddit + '/submit'
|
194
|
+
skip_over_18 if has_over_18
|
195
|
+
@browser.text_field(id: 'url').set url
|
196
|
+
@browser.textarea(name: 'title').set title
|
197
|
+
@browser.button(name: 'submit').click
|
198
|
+
wait_submit
|
199
|
+
end
|
200
|
+
|
201
|
+
def sub_has_flair
|
202
|
+
return !@browser.div('aria-label': 'Not available for this community').present?
|
203
|
+
end
|
204
|
+
|
205
|
+
def set_flair(flair)
|
206
|
+
return if !sub_has_flair
|
207
|
+
@browser.div('aria-label': 'Add flair').click
|
208
|
+
if flair == nil
|
209
|
+
@browser.div('aria-label': 'flair_picker').div.click
|
210
|
+
else
|
211
|
+
@browser.div('aria-label': 'flair_picker').span(text: flair).click
|
212
|
+
end
|
213
|
+
@browser.button(text: 'Apply').click
|
214
|
+
end
|
215
|
+
|
216
|
+
def submit_link_new(subreddit, url, title, flair = nil) #uses new reddit
|
217
|
+
@browser.goto 'https://www.reddit.com/r/' + subreddit + '/submit'
|
218
|
+
skip_over_18_new if has_over_18_new
|
219
|
+
blink = @browser.button(text: 'Link')
|
220
|
+
blink.click if blink.present?
|
221
|
+
@browser.textarea(placeholder: 'Title').set title
|
222
|
+
@browser.textarea(placeholder: 'Url').set url
|
223
|
+
set_flair(flair)
|
224
|
+
@browser.buttons(text: 'Post')[1].click
|
225
|
+
wait_submit
|
226
|
+
end
|
227
|
+
|
228
|
+
def submit_text(subreddit, title, text)
|
229
|
+
@browser.goto PAGE_SUBREDDIT + subreddit + '/submit?selftext=true'
|
230
|
+
skip_over_18 if has_over_18
|
231
|
+
@browser.textarea(name: 'title').set title
|
232
|
+
@browser.textarea(name: 'text').set text
|
233
|
+
@browser.button(name: 'submit').click
|
234
|
+
wait_submit
|
235
|
+
end
|
236
|
+
|
237
|
+
#---------------------------------------------------------------------------------
|
238
|
+
#Post handling
|
239
|
+
#---------------------------------------------------------------------------------
|
240
|
+
|
241
|
+
def is_original_post_voted(type)
|
242
|
+
div = @browser.div(id: 'siteTable').div(class: 'midcol')
|
243
|
+
case type
|
244
|
+
when 'up'
|
245
|
+
return div.attribute_value('class') == 'midcol likes'
|
246
|
+
when 'down'
|
247
|
+
return div.attribute_value('class') == 'midcol dislikes'
|
248
|
+
else
|
249
|
+
raise 'Unknown vote type: ' + type
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def get_original_post_vote
|
254
|
+
return 'up' if is_original_post_voted('up')
|
255
|
+
return 'down' if is_original_post_voted('down')
|
256
|
+
return nil
|
257
|
+
end
|
258
|
+
|
259
|
+
def vote_original_post(type)
|
260
|
+
return if is_original_post_voted(type)
|
261
|
+
div = @browser.div(id: 'siteTable').div(class: 'midcol')
|
262
|
+
div.div(class: type).click
|
263
|
+
end
|
264
|
+
|
265
|
+
def form_post_url(link)
|
266
|
+
return PAGE_MAIN_NO_SLASH + link
|
267
|
+
end
|
268
|
+
|
269
|
+
def open_post(post)
|
270
|
+
case post
|
271
|
+
when Hash
|
272
|
+
@browser.goto form_post_url(post['link'])
|
273
|
+
when String
|
274
|
+
@browser.goto post
|
275
|
+
else
|
276
|
+
return
|
277
|
+
end
|
278
|
+
skip_over_18 if has_over_18
|
279
|
+
end
|
280
|
+
|
281
|
+
def has_reply(answer)
|
282
|
+
form = @browser.form(text: answer)
|
283
|
+
return form.present? && form.parent.parent.attribute_value('data-author') == @username
|
284
|
+
end
|
285
|
+
|
286
|
+
def has_reply_error
|
287
|
+
return @browser.span(class: 'error', style: '').present?
|
288
|
+
end
|
289
|
+
|
290
|
+
def get_reply_error
|
291
|
+
return @browser.span(class: 'error', style: '').split(" ")[1]
|
292
|
+
end
|
293
|
+
|
294
|
+
def wait_reply(time = 2)
|
295
|
+
sleep time
|
296
|
+
return !has_reply_error
|
297
|
+
end
|
298
|
+
|
299
|
+
def reply_post(post, answer)
|
300
|
+
open_post(post)
|
301
|
+
@browser.div(class: 'commentarea').textarea(name: 'text').set answer
|
302
|
+
@browser.div(class: 'commentarea').button(text: 'save').click
|
303
|
+
return wait_reply
|
304
|
+
end
|
305
|
+
|
306
|
+
def get_comment_replies_count(div)
|
307
|
+
return div.attribute_value('data-replies').to_i
|
308
|
+
end
|
309
|
+
|
310
|
+
def comment_has_replies(div)
|
311
|
+
return div.attribute_value('data-replies') != '0'
|
312
|
+
end
|
313
|
+
|
314
|
+
def get_comment_author(div)
|
315
|
+
return div.attribute_value('data-author')
|
316
|
+
end
|
317
|
+
|
318
|
+
def get_comment_link(div)
|
319
|
+
return div.attribute_value('data-permalink')
|
320
|
+
end
|
321
|
+
|
322
|
+
def get_comment_content(div)
|
323
|
+
return div.div(class: 'usertext-body').text
|
324
|
+
end
|
325
|
+
|
326
|
+
def get_comment_karma(div, vote)
|
327
|
+
case vote
|
328
|
+
when 'up'
|
329
|
+
ind = 2
|
330
|
+
when 'down'
|
331
|
+
ind = 0
|
332
|
+
else
|
333
|
+
ind = 1
|
334
|
+
end
|
335
|
+
return div.p(class: 'tagline').spans(class: 'score')[ind].text.split(' ')[0].to_i
|
336
|
+
end
|
337
|
+
|
338
|
+
def is_comment_voted(div, type) #type is 'up' or 'down'
|
339
|
+
case type
|
340
|
+
when 'up'
|
341
|
+
buffer = 'likes'
|
342
|
+
when 'down'
|
343
|
+
buffer = 'dislikes'
|
344
|
+
else
|
345
|
+
raise 'Unknown vote type!'
|
346
|
+
end
|
347
|
+
return div.div(class: 'entry').attribute_value('class') == 'entry ' + buffer
|
348
|
+
end
|
349
|
+
|
350
|
+
def get_comment_vote(div)
|
351
|
+
return 'up' if is_comment_voted(div, 'up')
|
352
|
+
return 'down' if is_comment_voted(div, 'down')
|
353
|
+
return nil
|
354
|
+
end
|
355
|
+
|
356
|
+
def comment_has_karma(div)
|
357
|
+
return div.span(class: 'score').present?
|
358
|
+
end
|
359
|
+
|
360
|
+
def get_comment(div)
|
361
|
+
result = {}
|
362
|
+
result['author'] = get_comment_author(div)
|
363
|
+
result['link'] = get_comment_link(div)
|
364
|
+
result['content'] = get_comment_content(div)
|
365
|
+
result['vote'] = get_comment_vote(div)
|
366
|
+
result['karma'] = get_comment_karma(div, result['vote']) if comment_has_karma(div)
|
367
|
+
return result
|
368
|
+
end
|
369
|
+
|
370
|
+
def get_comments_divs
|
371
|
+
divs = @browser.div(class: 'commentarea').div(class: 'sitetable nestedlisting').children
|
372
|
+
result = []
|
373
|
+
divs.each do |div|
|
374
|
+
result.push div if div.attribute_value('data-type') == 'comment'
|
375
|
+
end
|
376
|
+
return result
|
377
|
+
end
|
378
|
+
|
379
|
+
def get_replies_divs(main_div)
|
380
|
+
divs = main_div.div(class: 'child').div.children
|
381
|
+
begin #calling length if the length is 0 causes an exception
|
382
|
+
x = divs.length
|
383
|
+
rescue
|
384
|
+
return []
|
385
|
+
end
|
386
|
+
result = []
|
387
|
+
divs.each do |div|
|
388
|
+
result.push div if div.attribute_value('data-type') == 'comment'
|
389
|
+
end
|
390
|
+
return result
|
391
|
+
end
|
392
|
+
|
393
|
+
def parse_comments_divs(divs)
|
394
|
+
result = []
|
395
|
+
divs.each do |div|
|
396
|
+
result.push get_comment(div)
|
397
|
+
if comment_has_replies(div)
|
398
|
+
result[result.length-1]['replies'] = parse_comments_divs(get_replies_divs(div))
|
399
|
+
end
|
400
|
+
end
|
401
|
+
return result
|
402
|
+
end
|
403
|
+
|
404
|
+
def expand_all_comments
|
405
|
+
while true
|
406
|
+
begin
|
407
|
+
span = @browser.span(class: 'morecomments')
|
408
|
+
span.present? ? span.click : return
|
409
|
+
rescue
|
410
|
+
end
|
411
|
+
sleep 0.5
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def get_comments(post, expand = false)
|
416
|
+
open_post(post)
|
417
|
+
expand_all_comments if expand
|
418
|
+
return parse_comments_divs(get_comments_divs)
|
419
|
+
end
|
420
|
+
|
421
|
+
def reply_comments(div, answer)
|
422
|
+
div.li(text: 'reply').click
|
423
|
+
div.textarea(name: 'text').set answer
|
424
|
+
div.button(class: 'save').click
|
425
|
+
end
|
426
|
+
|
427
|
+
def vote_comment(div, type)
|
428
|
+
return if is_comment_voted(div, type)
|
429
|
+
div.div(class: 'midcol').div(class: type).click
|
430
|
+
end
|
431
|
+
|
432
|
+
#---------------------------------------------------------------------------------
|
433
|
+
#Subreddit handling
|
434
|
+
#---------------------------------------------------------------------------------
|
435
|
+
|
436
|
+
def get_posts_divs
|
437
|
+
divs = @browser.div(id: 'siteTable').children
|
438
|
+
result = []
|
439
|
+
divs.each do |div|
|
440
|
+
result.push div if div.attribute_value('data-type') == 'link'
|
441
|
+
end
|
442
|
+
return result
|
443
|
+
end
|
444
|
+
|
445
|
+
def get_post_author(div)
|
446
|
+
return div.attribute_value('data-author')
|
447
|
+
end
|
448
|
+
|
449
|
+
def get_post_link(div)
|
450
|
+
return div.attribute_value('data-permalink')
|
451
|
+
end
|
452
|
+
|
453
|
+
def get_post_karma(div)
|
454
|
+
return div.attribute_value('data-score').to_i
|
455
|
+
end
|
456
|
+
|
457
|
+
def get_post_title(div)
|
458
|
+
return div.link(class: 'title').text
|
459
|
+
end
|
460
|
+
|
461
|
+
def get_post_number_of_comments(div)
|
462
|
+
return div.attribute_value('data-comments-count').to_i
|
463
|
+
end
|
464
|
+
|
465
|
+
def isPostVoted(div, type)
|
466
|
+
return is_comment_voted(div, type)
|
467
|
+
end
|
468
|
+
|
469
|
+
def get_post_vote(div)
|
470
|
+
return get_comment_vote(div)
|
471
|
+
end
|
472
|
+
|
473
|
+
def vote_post(div, type)
|
474
|
+
vote_comment(div, type)
|
475
|
+
end
|
476
|
+
|
477
|
+
def get_post(div)
|
478
|
+
result = {}
|
479
|
+
result['author'] = get_post_author(div)
|
480
|
+
result['link'] = get_post_link(div)
|
481
|
+
result['karma'] = get_post_karma(div)
|
482
|
+
result['title'] = get_post_title(div)
|
483
|
+
result['vote'] = get_post_vote(div)
|
484
|
+
result['number_of_comments'] = get_post_number_of_comments(div)
|
485
|
+
return result
|
486
|
+
end
|
487
|
+
|
488
|
+
def subreddit_move_page(direction)
|
489
|
+
return message_move_page(direction)
|
490
|
+
end
|
491
|
+
|
492
|
+
def form_subreddit_url(name, subpage = 'hot')
|
493
|
+
return PAGE_SUBREDDIT + name + '/' + subpage
|
494
|
+
end
|
495
|
+
|
496
|
+
def open_subreddit(subreddit, subpage = 'hot')
|
497
|
+
raise 'Unknown subreddit subpage: ' + subpage if !SUBREDDIT_SUBPAGES.include? subpage
|
498
|
+
case subreddit
|
499
|
+
when Hash
|
500
|
+
@browser.goto form_subreddit_url(subreddit['name'], subpage)
|
501
|
+
when String
|
502
|
+
if subreddit.include? '/'
|
503
|
+
@browser.goto subreddit
|
504
|
+
else
|
505
|
+
@browser.goto form_subreddit_url(subreddit, subpage)
|
506
|
+
end
|
507
|
+
else
|
508
|
+
return
|
509
|
+
end
|
510
|
+
skip_over_18 if has_over_18
|
511
|
+
end
|
512
|
+
|
513
|
+
def get_posts(subreddit, subpage = 'hot', max_pages = 1)
|
514
|
+
raise 'Unknown subreddit subpage: ' + subpage if !SUBREDDIT_SUBPAGES.include? subpage
|
515
|
+
open_subreddit(subreddit, subpage)
|
516
|
+
result = []
|
517
|
+
count = 0
|
518
|
+
while true
|
519
|
+
get_posts_divs.each do |div|
|
520
|
+
result.push get_post(div)
|
521
|
+
end
|
522
|
+
count += 1
|
523
|
+
break if count >= max_pages
|
524
|
+
break if !subreddit_move_page('next')
|
525
|
+
end
|
526
|
+
return result
|
527
|
+
end
|
528
|
+
|
529
|
+
def form_subreddit_mod_url(subreddit)
|
530
|
+
case subreddit
|
531
|
+
when Hash
|
532
|
+
return PAGE_SUBREDDIT + subreddit['name'] + '/about/moderators'
|
533
|
+
when String
|
534
|
+
return PAGE_SUBREDDIT + subreddit + '/about/moderators'
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
def get_moderators(subreddit) #an array of user names, not user structs itself
|
539
|
+
@browser.goto form_subreddit_mod_url(subreddit)
|
540
|
+
spans = @browser.div(class: 'moderator-table').spans(class: 'user')
|
541
|
+
result = []
|
542
|
+
spans.each do |span|
|
543
|
+
result.push span.link.text
|
544
|
+
end
|
545
|
+
return result
|
546
|
+
end
|
547
|
+
|
548
|
+
def get_subscribers
|
549
|
+
return @browser.span(class: 'subscribers').span(class: 'number').text.gsub(',', '').to_i
|
550
|
+
end
|
551
|
+
|
552
|
+
def get_users_online
|
553
|
+
return @browser.p(class: 'users-online').span(class: 'number').text.gsub(',', '').to_i
|
554
|
+
end
|
555
|
+
|
556
|
+
def get_side_bar
|
557
|
+
return @browser.div(class: 'usertext-body').text
|
558
|
+
end
|
559
|
+
|
560
|
+
def get_subreddit(subreddit)
|
561
|
+
result = {}
|
562
|
+
result['name'] = subreddit
|
563
|
+
open_subreddit(subreddit)
|
564
|
+
result['subscribers'] = get_subscribers
|
565
|
+
result['users_online'] = get_users_online
|
566
|
+
result['sidebar'] = get_side_bar
|
567
|
+
result['moderators'] = get_moderators(subreddit)
|
568
|
+
return result
|
569
|
+
end
|
570
|
+
|
571
|
+
def did_create_sub
|
572
|
+
return @browser.p(text: 'your subreddit has been created').present?
|
573
|
+
end
|
574
|
+
|
575
|
+
def wait_sub_creation
|
576
|
+
count = 0.0
|
577
|
+
while true
|
578
|
+
return true if did_create_sub
|
579
|
+
sleep 0.25
|
580
|
+
count += 0.25
|
581
|
+
return false if count >= 10
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
def create_subreddit(subreddit)
|
586
|
+
@browser.goto CREATE_SUB_PAGE
|
587
|
+
@browser.text_field(id: 'name').set subreddit.name
|
588
|
+
@browser.text_field(id: 'title').set subreddit.title
|
589
|
+
@browser.textarea(name: 'public_description').set subreddit.description
|
590
|
+
@browser.textarea(name: 'description').set subreddit.sidebar
|
591
|
+
@browser.textarea(name: 'submit_text').set subreddit.subtext
|
592
|
+
@browser.radio(id: SUB_TYPES[subreddit.type]).set
|
593
|
+
@browser.radio(id: CONTENT_OPTIONS[subreddit.content]).set
|
594
|
+
@browser.button(text: 'create').click
|
595
|
+
return wait_sub_creation
|
596
|
+
end
|
597
|
+
|
598
|
+
#---------------------------------------------------------------------------------
|
599
|
+
#User handling
|
600
|
+
#---------------------------------------------------------------------------------
|
601
|
+
|
602
|
+
def get_user_post_karma
|
603
|
+
return @browser.span(class: 'karma').text.gsub(',', '').to_i
|
604
|
+
end
|
605
|
+
|
606
|
+
def get_user_comment_karma
|
607
|
+
return @browser.spans(class: 'karma')[1].text.gsub(',', '').to_i
|
608
|
+
end
|
609
|
+
|
610
|
+
def is_moderator
|
611
|
+
return @browser.ul(id: 'side-mod-list').present?
|
612
|
+
end
|
613
|
+
|
614
|
+
def get_moderating
|
615
|
+
result = []
|
616
|
+
@browser.ul(id: 'side-mod-list').lis.each do |li|
|
617
|
+
result.push li.link.title.split('/')[1]
|
618
|
+
end
|
619
|
+
return result
|
620
|
+
end
|
621
|
+
|
622
|
+
def is_friend
|
623
|
+
return @browser.span(class: 'fancy-toggle-button').link(text: '- friends').attribute_value('class').include?('active')
|
624
|
+
end
|
625
|
+
|
626
|
+
def add_friend
|
627
|
+
return if is_friend
|
628
|
+
@browser.link(text: '+ friends').click
|
629
|
+
end
|
630
|
+
|
631
|
+
def remove_friend
|
632
|
+
return if !is_friend
|
633
|
+
@browser.link(text: '- friends').click
|
634
|
+
end
|
635
|
+
|
636
|
+
def open_user_page(user)
|
637
|
+
@browser.goto PAGE_USER + user
|
638
|
+
skip_over_18 if has_over_18
|
639
|
+
end
|
640
|
+
|
641
|
+
def get_user(user)
|
642
|
+
open_user_page(user)
|
643
|
+
return nil if @browser.div(id: 'classy-error').present?
|
644
|
+
result = {}
|
645
|
+
result['name'] = user
|
646
|
+
result['post_karma'] = get_user_post_karma
|
647
|
+
result['comment_karma'] = get_user_comment_karma
|
648
|
+
result['is_friend'] = @username ? is_friend : false
|
649
|
+
result['moderating'] = get_moderating if is_moderator
|
650
|
+
return result
|
651
|
+
end
|
652
|
+
|
653
|
+
#---------------------------------------------------------------------------------
|
654
|
+
#User Activity handling
|
655
|
+
#---------------------------------------------------------------------------------
|
656
|
+
|
657
|
+
def get_activity_divs
|
658
|
+
divs = @browser.div(id: 'siteTable').children
|
659
|
+
result = []
|
660
|
+
divs.each do |div|
|
661
|
+
result.push div if div.id.include? 'thing'
|
662
|
+
end
|
663
|
+
return result
|
664
|
+
end
|
665
|
+
|
666
|
+
def get_activity_type(div)
|
667
|
+
return div.attribute_value('data-type')
|
668
|
+
end
|
669
|
+
|
670
|
+
def get_activity_link(div)
|
671
|
+
return div.attribute_value('data-permalink')
|
672
|
+
end
|
673
|
+
|
674
|
+
def get_activity_subreddit(div)
|
675
|
+
return div.attribute_value('data-subreddit')
|
676
|
+
end
|
677
|
+
|
678
|
+
def get_activity_title(div)
|
679
|
+
return div.link(class: 'title').text
|
680
|
+
end
|
681
|
+
|
682
|
+
def get_activity_content(div) #only for comments
|
683
|
+
return div.div(class: 'usertext-body').text
|
684
|
+
end
|
685
|
+
|
686
|
+
def get_activity_karma(div, vote)
|
687
|
+
case get_activity_type(div)
|
688
|
+
when 'comment'
|
689
|
+
return get_comment_karma(div, vote)
|
690
|
+
when 'link'
|
691
|
+
case vote
|
692
|
+
when 'up'
|
693
|
+
return div.div(class: 'score likes').title.to_i
|
694
|
+
when 'down'
|
695
|
+
return div.div(class: 'score dislikes').title.to_i
|
696
|
+
else
|
697
|
+
return div.div(class: 'score unvoted').title.to_i
|
698
|
+
end
|
699
|
+
else
|
700
|
+
raise 'Unknown activity type!'
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
def is_activity_voted(div, type)
|
705
|
+
return is_comment_voted(div, type)
|
706
|
+
end
|
707
|
+
|
708
|
+
def get_activity_vote(div)
|
709
|
+
return get_comment_vote(div)
|
710
|
+
end
|
711
|
+
|
712
|
+
def vote_activity(div, type)
|
713
|
+
vote_message(div, type)
|
714
|
+
end
|
715
|
+
|
716
|
+
def get_activity(div)
|
717
|
+
result = {}
|
718
|
+
result['type'] = get_activity_type(div)
|
719
|
+
result['link'] = get_activity_link(div)
|
720
|
+
result['subreddit'] = get_activity_subreddit(div)
|
721
|
+
result['title'] = get_activity_title(div)
|
722
|
+
result['content'] = result['type'] == 'link' ? result['title'] : get_activity_content(div)
|
723
|
+
result['vote'] = get_activity_vote(div)
|
724
|
+
result['karma'] = get_activity_karma(div, result['vote'])
|
725
|
+
return result
|
726
|
+
end
|
727
|
+
|
728
|
+
def user_move_page(direction)
|
729
|
+
return message_move_page(direction)
|
730
|
+
end
|
731
|
+
|
732
|
+
def get_user_activities(user, sortby = 'new', max_pages = 1)
|
733
|
+
raise 'Unknown user sortby: ' + sortby if !USER_SORTBYS.include? sortby
|
734
|
+
@browser.goto PAGE_USER + user + '/?sort=' + sortby
|
735
|
+
result = []
|
736
|
+
count = 0
|
737
|
+
while true
|
738
|
+
get_activity_divs.each do |div|
|
739
|
+
result.push get_activity(div)
|
740
|
+
end
|
741
|
+
count += 1
|
742
|
+
break if count >= max_pages
|
743
|
+
break if !user_move_page('next')
|
744
|
+
end
|
745
|
+
return result
|
746
|
+
end
|
747
|
+
|
748
|
+
#---------------------------------------------------------------------------------
|
749
|
+
#Extra functions
|
750
|
+
#---------------------------------------------------------------------------------
|
751
|
+
|
752
|
+
def get_banned_subreddits
|
753
|
+
msgs = get_messages('messages', true)
|
754
|
+
result = []
|
755
|
+
msgs.each do |msg|
|
756
|
+
result.push msg['author'] if msg['content'].include?('You have been permanently banned')
|
757
|
+
end
|
758
|
+
return result
|
759
|
+
end
|
760
|
+
|
761
|
+
def phase_pick_subs
|
762
|
+
@browser.execute_script("var buttons = document.getElementsByClassName('c-btn c-btn-primary subreddit-picker__subreddit-button');\nfor (var i = 0; i < 8; i++) {\nbuttons[i].click();\n}")
|
763
|
+
end
|
764
|
+
|
765
|
+
def phase_enter_data(username, password, captcha_token)
|
766
|
+
result = {}
|
767
|
+
if username == nil
|
768
|
+
link = @browser.link(class: 'username-generator__item')
|
769
|
+
result['username'] = link.text
|
770
|
+
link.click
|
771
|
+
else
|
772
|
+
result['username'] = username
|
773
|
+
@browser.text_field(id: 'user_reg').set username
|
774
|
+
end
|
775
|
+
@browser.text_field(id: 'passwd_reg').set password
|
776
|
+
result['password'] = password
|
777
|
+
sleep 5
|
778
|
+
@browser.execute_script(%{document.getElementById("g-recaptcha-response").innerHTML="} + captcha_token + %{"})
|
779
|
+
sleep 5
|
780
|
+
return result
|
781
|
+
end
|
782
|
+
|
783
|
+
def move_phase
|
784
|
+
@browser.buttons(text: 'Submit').each do |button|
|
785
|
+
if button.present?
|
786
|
+
button.click
|
787
|
+
sleep 5
|
788
|
+
return
|
789
|
+
end
|
790
|
+
end
|
791
|
+
@browser.buttons(text: 'Next').each do |button|
|
792
|
+
if button.present?
|
793
|
+
button.click
|
794
|
+
sleep 5
|
795
|
+
return
|
796
|
+
end
|
797
|
+
end
|
798
|
+
end
|
799
|
+
|
800
|
+
def get_phase
|
801
|
+
return @browser.button(class: 'c-btn c-btn-primary subreddit-picker__subreddit-button').present? ? 'picksubs' : 'enterdata'
|
802
|
+
end
|
803
|
+
|
804
|
+
def create_account(username, password, captcha_token) #if username is nil, selects username from reddit's suggestions
|
805
|
+
@browser.goto PAGE_MAIN
|
806
|
+
@browser.div(id: 'header-bottom-right').link(text: 'sign up').click
|
807
|
+
sleep 3
|
808
|
+
@browser.button(text: 'Next').click
|
809
|
+
sleep 5
|
810
|
+
result = nil
|
811
|
+
2.times do
|
812
|
+
case get_phase
|
813
|
+
when 'picksubs'
|
814
|
+
phase_pick_subs
|
815
|
+
when 'enterdata'
|
816
|
+
break if result != nil
|
817
|
+
result = phase_enter_data(username, password, captcha_token)
|
818
|
+
end
|
819
|
+
move_phase
|
820
|
+
end
|
821
|
+
wait_login(result['username'])
|
822
|
+
return result
|
823
|
+
end
|
824
|
+
|
825
|
+
def is_user_banned(user)
|
826
|
+
@browser.goto PAGE_USER + user
|
827
|
+
skip_over_18 if has_over_18
|
828
|
+
return @browser.div(id: 'classy-error').present? || @browser.h3(text: 'This account has been suspended').present?
|
829
|
+
end
|
830
|
+
|
831
|
+
def is_subreddit_banned(subr)
|
832
|
+
@browser.goto PAGE_SUBREDDIT + subr
|
833
|
+
skip_over_18 if has_over_18
|
834
|
+
return @browser.h3(text: 'This community has been banned').present?
|
835
|
+
end
|
836
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reddit_auto
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ícaro Augusto
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: watir
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.16.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.16.5
|
27
|
+
description: This gem uses watir and selenium to automate almost all reddit features
|
28
|
+
using a headless browser.
|
29
|
+
email:
|
30
|
+
- icaro10100@hotmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/reddit_auto.rb
|
36
|
+
homepage: https://github.com/IcaroAugusto/reddit_automation_bot
|
37
|
+
licenses:
|
38
|
+
- MIT
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 2.3.0
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubygems_version: 3.1.2
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: A reddit automation gem that uses watir/selenium
|
59
|
+
test_files: []
|