reddit-to-telegram 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e02f076f3c1b878452f2b871436a34c0b195fea3047f2e85dcb9c8af90a9205
4
- data.tar.gz: 544ab98c2f0599d01429956f5ca0ffef0af593272c929f3652a10cfb3e4f1b6f
3
+ metadata.gz: 81b0c79fce533367384f2a5bce697b0fafa0e914f772ce23e7ac903aeeec1577
4
+ data.tar.gz: aac0c0448ab4964df36acf62819e2c73e1d7f90d750091fa88b43a1605612580
5
5
  SHA512:
6
- metadata.gz: fdedc48421d1398506c6b4f1e0841e8173db20db22083e1a4861b8f0177eb40bd17091eb24728031ca04bb146e72a9682953637b302df58f8f561ee06eef3e1e
7
- data.tar.gz: d1bc0f40dca38bc40d0180056b79f169d88082f1345961ebb89e86d0d521e10109cda6c7614edf110b7af16905f39dc53fe7c8fdc2615f1e0dd5511a25cf9e50
6
+ metadata.gz: 1b0f0c32452b4784a50ed4f0557bf1347d84e0fb3f0bc6ba6af1baa0c2426f066283ad9c0a30f22e1232462c2aa2e45f44e637e942208548130152bf5611d081
7
+ data.tar.gz: 54807fe38f1a4f03e6baff998dc517b1fd3835f435837e75e1ca23809df7b5edef768106a4477f43f9490af63d3df280273e420c153004760e7f19680856aa8b
data/.rubocop.yml CHANGED
@@ -1,6 +1,9 @@
1
1
  Metrics/MethodLength:
2
2
  Max: 20
3
3
 
4
+ Naming/VariableNumber:
5
+ Enabled: false
6
+
4
7
  Style/Documentation:
5
8
  Enabled: false
6
9
 
@@ -15,3 +18,6 @@ Style/StringLiterals:
15
18
 
16
19
  Style/RaiseArgs:
17
20
  Enabled: false
21
+
22
+ Style/RedundantRegexpEscape:
23
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reddit-to-telegram (0.2.1)
4
+ reddit-to-telegram (0.5.0)
5
5
  aws-sdk-simpledb
6
6
  httparty
7
7
 
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  #### Fetches hot posts from chosen subreddits and pushes them to Telegram channels.
6
6
 
7
- Beware, this is remotely not production-ready, you'll see lots of bugs and it may break at any time.
7
+ Beware, this is remotely not production-ready, API will change, you'll see lots of bugs and it may break at any time.
8
8
  Be sure to check for gem updates.
9
9
 
10
10
  ## Installation
@@ -23,21 +23,21 @@ Or `gem install reddit-to-telegram`. Don't forget to `require` it.
23
23
 
24
24
  To run it, you'll need some env variables set.
25
25
  ```
26
- RTT_AWS_ACCESS_KEY_ID= # Your AWS access key ID. Needed for AWS SimpleDB storage.
26
+ RTT_AWS_ACCESS_KEY_ID= # (Optional) Your AWS access key ID. Needed for AWS SimpleDB storage.
27
27
  RTT_AWS_DOMAIN_NAME= # (Optional) Domain name to use for SimpleDB
28
- RTT_AWS_REGION= # AWS region your SimpleDB will be hosted on. Beware, it's not available in all regions.
29
- RTT_AWS_SECRET_ACCESS_KEY= # Your AWS access key ID. Needed for AWS SimpleDB storage.
28
+ RTT_AWS_REGION= # (Optional) AWS region your SimpleDB will be hosted on. Beware, it's not available in all regions.
29
+ RTT_AWS_SECRET_ACCESS_KEY= # (Optional) Your AWS access key ID. Needed for AWS SimpleDB storage.
30
30
  RTT_MAX_STORED_POSTS= # (Optional) Number of posts to store in the database to avoid duplicates, default is 25.
31
31
  RTT_REDDIT_CLIENT_ID= # Reddit app credentials to access API. Might not be needed depending on setup, reddit allows some requests without authentication.
32
32
  RTT_REDDIT_CLIENT_SECRET= # Reddit app credentials to access API. Might not be needed depending on setup, reddit allows some requests without authentication.
33
- RTT_STORE_TYPE= # (Optional) Choose between aws_simple_db, memory or temp_file
33
+ RTT_STORE_TYPE= # (Optional) Choose between aws_simple_db, memory or temp_file. Default is aws_simple_db, so if you're not specifying your AWS credentials, you have to specify another store type.
34
34
  RTT_TELEGRAM_BOT_TOKEN= # The token you've received when you've created a telegram bot.
35
35
  RTT_TEMP_DIR= (Optional) # Directory to write temp files to without trailing /
36
36
  ```
37
37
 
38
38
  You can also set them dynamically:
39
39
  ```
40
- RedditToTelegram::Variables.aws.aws_access_key_id =
40
+ RedditToTelegram::Variables.aws.access_key_id =
41
41
  RedditToTelegram::Variables.telegram.bot_token =
42
42
  ```
43
43
  Check out `lib/variables` for list of all available variables.
@@ -45,12 +45,15 @@ Check out `lib/variables` for list of all available variables.
45
45
  ## Usage
46
46
 
47
47
  1. Add the bot as administrator to Telegram channels you'd like to post to.
48
- 2.
48
+ 2. To fetch latest hot post which hasn't been pushed yet:
49
49
  ```
50
- RedditToTelegram.post(
50
+ RedditToTelegram.hot(
51
51
  subreddit_name_1: :telegram_channel_id_1,
52
52
  subreddit_name_2: :telegram_channel_id_2
53
53
  )
54
-
54
+ ```
55
+ Or to push one specific post (the only thing you need to set up for this is your telegram bot token):
56
+ ```
57
+ RedditToTelegram.single("regular_link_to_post", :telegram_channel_id)
55
58
  ```
56
59
  Use `:telegram_channel_id` without the `@`.
@@ -1,37 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "reddit_to_telegram/reddit/fetch"
4
- require_relative "reddit_to_telegram/store"
5
- require_relative "reddit_to_telegram/telegram/post"
3
+ require_relative "reddit_to_telegram/post"
6
4
  require_relative "reddit_to_telegram/version"
7
5
 
8
6
  module RedditToTelegram
9
7
  class << self
10
- def post(sources)
11
- return if sources.empty?
8
+ extend Forwardable
12
9
 
13
- Store.setup
10
+ def_delegators :post, :hot, :single
14
11
 
15
- sources.each do |subreddit, telegram_chat_id|
16
- res = Reddit::Fetch.hot(subreddit)
17
- handle_res(res, subreddit, telegram_chat_id)
18
- end
12
+ def post
13
+ Post
19
14
  end
20
15
 
21
- private
22
-
23
- def handle_res(res, subreddit, telegram_chat_id)
24
- return if res.nil?
25
-
26
- post = find_new_post(subreddit, res)
27
- return if post.nil?
28
-
29
- Telegram::Post.push(post, telegram_chat_id)
30
- Store::Posts.add(subreddit, post[:id])
31
- end
32
-
33
- def find_new_post(subreddit, posts)
34
- posts.find { |post| !Store::Posts.dup?(subreddit, post[:id]) }
16
+ def version
17
+ VERSION
35
18
  end
36
19
  end
37
20
  end
@@ -3,4 +3,5 @@
3
3
  module RedditToTelegram
4
4
  class RedditToTelegramError < StandardError; end
5
5
  class InvalidStoreType < RedditToTelegramError; end
6
+ class MissingVariables < RedditToTelegramError; end
6
7
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "reddit/fetch"
4
+ require_relative "store"
5
+ require_relative "telegram/post"
6
+ require_relative "variables"
7
+
8
+ module RedditToTelegram
9
+ class Post
10
+ class << self
11
+ def hot(sources)
12
+ return if sources.empty?
13
+
14
+ Store.setup
15
+
16
+ sources.each do |subreddit, telegram_chat_id|
17
+ res = Reddit::Fetch.hot(subreddit)
18
+ handle_res(res, subreddit, telegram_chat_id)
19
+ end
20
+ end
21
+
22
+ def single(link, telegram_chat_id)
23
+ return if link.empty?
24
+
25
+ Variables.store.type = :memory
26
+ Store.setup
27
+
28
+ res = Reddit::Fetch.post(link)
29
+ Telegram::Post.push(res, telegram_chat_id)
30
+ end
31
+
32
+ private
33
+
34
+ def handle_res(res, subreddit, telegram_chat_id)
35
+ return if res.nil?
36
+
37
+ post = find_new_post(subreddit, res)
38
+ return if post.nil?
39
+
40
+ res = Telegram::Post.push(post, telegram_chat_id)
41
+ Store::Posts.add(subreddit, post[:id])
42
+ res
43
+ end
44
+
45
+ def find_new_post(subreddit, posts)
46
+ posts.find { |post| !Store::Posts.dup?(subreddit, post[:id]) }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -12,7 +12,9 @@ module RedditToTelegram
12
12
  include HTTParty
13
13
 
14
14
  BASE_URI = "https://oauth.reddit.com/r"
15
- QUERY = { g: "GLOBAL", limit: Variables.store.max_stored_posts }.freeze
15
+ WEBSITE_URI = "https://www.reddit.com/r"
16
+ QUERY_FOR_POST = { g: "GLOBAL" }.freeze
17
+ QUERY_FOR_SUBREDDIT = QUERY_FOR_POST.merge(limit: Variables.store.max_stored_posts).freeze
16
18
  BASE_HEADERS = {
17
19
  "Content-Type" => "application/json",
18
20
  "Accept" => "application/json"
@@ -23,26 +25,50 @@ module RedditToTelegram
23
25
  headers = BASE_HEADERS.merge("Authorization" => "Bearer #{Store::Reddit.token}")
24
26
  res = HTTParty.get(
25
27
  "#{BASE_URI}/#{subreddit}/hot.json",
26
- query: QUERY,
28
+ query: QUERY_FOR_SUBREDDIT,
27
29
  headers:
28
30
  )
29
- handle_response(res, subreddit, retries_left)
31
+ handle_response(res, hot: [subreddit, retries_left])
32
+ end
33
+
34
+ def post(link, retries_left = 5)
35
+ headers = BASE_HEADERS.merge("Authorization" => "Bearer #{Store::Reddit.token}")
36
+ res = HTTParty.get(
37
+ "#{link.gsub('www', 'oauth')}.json",
38
+ query: QUERY_FOR_POST,
39
+ headers:
40
+ )
41
+ handle_response(res, post: retries_left)
30
42
  end
31
43
 
32
44
  private
33
45
 
34
- def handle_response(res, subreddit, retries_left)
46
+ def handle_response(res, func)
47
+ func_name = func.keys.first
48
+ func_args = Array(func.values.first)
49
+
35
50
  case res.code
36
51
  when 401
37
- Store::Reddit.token = Auth.token
38
- hot(subreddit, retries_left) if retries_left > 0
52
+ handle_401(func_name, func_args)
39
53
  when 429
40
- sleep(10 / retries_left) if retries_left > 0
41
- hot(subreddit, retries_left - 1) if retries_left > 0
54
+ handle_429(func_name, func_args)
42
55
  when 200
43
56
  Output.format_response(res)
44
57
  end
45
58
  end
59
+
60
+ def handle_401(func_name, func_args)
61
+ Store::Reddit.token = Auth.token
62
+ send(func_name, *func_args) if func_args.last > 0
63
+ end
64
+
65
+ def handle_429(func_name, func_args)
66
+ retries_left = func_args.last
67
+
68
+ sleep(10 / retries_left) if retries_left > 0
69
+ func_args[func_args.length - 1] = retries_left - 1
70
+ send(func_name, *func_args) if retries_left > 0
71
+ end
46
72
  end
47
73
  end
48
74
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+
5
+ module RedditToTelegram
6
+ module Reddit
7
+ class Output
8
+ class Imgur
9
+ class << self
10
+ def try_extract(data)
11
+ full_url = decode_imgur_url(data)
12
+ video_url = extract_video_url(full_url)
13
+ width = extract_video_width(full_url)
14
+ return if video_url.nil? || width.nil?
15
+
16
+ format_imgur_post(data, video_url, width)
17
+ end
18
+
19
+ private
20
+
21
+ def decode_imgur_url(data)
22
+ encoded_url = data
23
+ .dig("media_embed", "content")
24
+ &.match(/src=\"\S+schema=imgur\"/)&.to_s
25
+ &.gsub(/src=\"|\"/, 'src=\"' => "", '\"' => "")
26
+ CGI.unescape(encoded_url)
27
+ end
28
+
29
+ def extract_video_url(full_url)
30
+ video_url_arr = full_url&.match(/image=(\S+\?|&)/)&.to_s&.send(:[], 6..-2)&.split(".")
31
+ return if Array(video_url_arr).empty?
32
+
33
+ video_url_arr[video_url_arr.length - 1] = "mp4"
34
+ video_url_arr.join(".")
35
+ end
36
+
37
+ def extract_video_width(full_url)
38
+ full_url&.match(/w=(\w+)/)&.to_s&.send(:[], 2..)
39
+ end
40
+
41
+ def format_imgur_post(data, video_url, width)
42
+ RedditToTelegram::Reddit::Output.send(
43
+ :base_post_format_attrs, data
44
+ ).merge(
45
+ {
46
+ type: :video,
47
+ media: video_url,
48
+ misc: {
49
+ binary: false,
50
+ video_width: width
51
+ }
52
+ }
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,10 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "cgi"
4
+ require_relative "output/imgur"
5
+
3
6
  module RedditToTelegram
4
7
  module Reddit
5
8
  class Output
6
9
  class << self
7
10
  def format_response(res)
11
+ return format_post(res[0]["data"]["children"][0]) if single_post?(res)
12
+
8
13
  posts = res["data"]["children"]
9
14
  posts.reject! { |post| post["data"]["stickied"] == true }
10
15
  posts.map { |post| format_post(post) }.compact
@@ -12,6 +17,10 @@ module RedditToTelegram
12
17
 
13
18
  private
14
19
 
20
+ def single_post?(res)
21
+ res.parsed_response.is_a?(Array) && res[0]["data"]["dist"] == 1
22
+ end
23
+
15
24
  def format_post(post)
16
25
  data = post["data"]
17
26
  if data["post_hint"] == "image"
@@ -33,6 +42,9 @@ module RedditToTelegram
33
42
  end
34
43
 
35
44
  def format_link_post(data)
45
+ res = Imgur.try_extract(data) if data["domain"] == "imgur.com"
46
+ return res unless res.nil?
47
+
36
48
  base_post_format_attrs(data).merge(
37
49
  { type: :text,
38
50
  text: "#{data['title']}\n\n#{data['url']}" }
@@ -61,7 +73,9 @@ module RedditToTelegram
61
73
  type: :video,
62
74
  media: video_url,
63
75
  misc: {
64
- binary: video_data["has_audio"] || false
76
+ binary: video_data["has_audio"] || false,
77
+ video_height: video_data["height"],
78
+ video_width: video_data["width"]
65
79
  }
66
80
  }
67
81
  )
@@ -69,12 +83,14 @@ module RedditToTelegram
69
83
 
70
84
  def base_post_format_attrs(data)
71
85
  { id: data["name"],
72
- text: data["title"] }
86
+ text: CGI.unescapeHTML(data["title"]) }
73
87
  end
74
88
 
75
89
  def prepare_gallery_links(data)
76
- data["media_metadata"].map do |image|
77
- image[1]["p"][0]["u"].split("?").first.gsub("preview", "i")
90
+ data["gallery_data"]["items"].map do |image|
91
+ data["media_metadata"].find do |key, _|
92
+ key == image["media_id"]
93
+ end[1]["s"]["u"].split("?").first.gsub("preview", "i")
78
94
  end
79
95
  end
80
96
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "aws-sdk-simpledb"
4
4
  require "json"
5
+ require_relative "../errors"
5
6
  require_relative "../variables"
6
7
 
7
8
  module RedditToTelegram
@@ -23,10 +24,20 @@ module RedditToTelegram
23
24
  attr_reader :reddit_token
24
25
 
25
26
  def setup
27
+ check_credentials
26
28
  create_domain unless client.list_domains.domain_names.include?(Variables.aws.domain_name)
27
29
  read_db
28
30
  end
29
31
 
32
+ def check_credentials
33
+ return unless Variables.store.type == :aws_simple_db
34
+
35
+ return if Variables.aws.all_present?
36
+
37
+ raise(MissingVariables.new("Missing AWS credentials. "\
38
+ "Set them up or change store type to anything other than aws_simple_db"))
39
+ end
40
+
30
41
  def reddit_token=(val)
31
42
  @reddit_token = val
32
43
  write_db
@@ -22,13 +22,14 @@ module RedditToTelegram
22
22
 
23
23
  class << self
24
24
  def push(post, channel)
25
- HTTParty.post(
25
+ res = HTTParty.post(
26
26
  "#{BASE_URI}#{Variables.telegram.bot_token}/send#{METHOD_MAP[post[:type]]}",
27
27
  **params(post, channel)
28
28
  )
29
29
 
30
30
  push({ type: :text, id: post[:id], text: post[:text] }, channel) if post[:type] == :gallery
31
31
  Video.delete_file if post[:type] == :video && post.dig(:misc)&.dig(:binary)
32
+ res
32
33
  end
33
34
 
34
35
  private
@@ -15,7 +15,13 @@ module RedditToTelegram
15
15
  when :text
16
16
  { chat_id: "@#{chat_id}", text: prepare_text(post, chat_id) }
17
17
  when :video
18
- { chat_id: "@#{chat_id}", video: prepare_video(post), caption: prepare_text(post, chat_id) }
18
+ {
19
+ chat_id: "@#{chat_id}",
20
+ video: prepare_video(post),
21
+ height: post[:misc][:video_height],
22
+ width: post[:misc][:video_width],
23
+ caption: prepare_text(post, chat_id)
24
+ }
19
25
  end
20
26
  end
21
27
 
@@ -42,10 +42,11 @@ module RedditToTelegram
42
42
  end
43
43
 
44
44
  class AWS
45
+ ATTRS = %i[access_key_id secret_access_key region domain_name].freeze
45
46
  DEFAULT_DOMAIN_NAME = "reddit_to_telegram"
46
47
 
47
48
  class << self
48
- attr_writer :access_key_id, :secret_access_key, :region, :domain_name
49
+ attr_writer(*ATTRS)
49
50
 
50
51
  def access_key_id
51
52
  @access_key_id ||= ENV["RTT_AWS_ACCESS_KEY_ID"]
@@ -62,6 +63,10 @@ module RedditToTelegram
62
63
  def domain_name
63
64
  @domain_name ||= ENV["RTT_AWS_DOMAIN_NAME"] || DEFAULT_DOMAIN_NAME
64
65
  end
66
+
67
+ def set_up?
68
+ ATTRS.all? { |a| !a.empty? }
69
+ end
65
70
  end
66
71
  end
67
72
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RedditToTelegram
4
- VERSION = "0.3.1"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reddit-to-telegram
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Tityuk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-29 00:00:00.000000000 Z
11
+ date: 2024-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-simpledb
@@ -66,9 +66,11 @@ files:
66
66
  - README.md
67
67
  - lib/reddit-to-telegram.rb
68
68
  - lib/reddit_to_telegram/errors.rb
69
+ - lib/reddit_to_telegram/post.rb
69
70
  - lib/reddit_to_telegram/reddit/auth.rb
70
71
  - lib/reddit_to_telegram/reddit/fetch.rb
71
72
  - lib/reddit_to_telegram/reddit/output.rb
73
+ - lib/reddit_to_telegram/reddit/output/imgur.rb
72
74
  - lib/reddit_to_telegram/store.rb
73
75
  - lib/reddit_to_telegram/store/aws_simple_db.rb
74
76
  - lib/reddit_to_telegram/store/memory.rb