reddit-to-telegram 0.3.1 → 0.5.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 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