UrlCategorise 0.1.7 → 0.1.11
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/.claude/settings.local.json +9 -2
- data/.gitignore +1 -0
- data/.rubocop.yml +56 -0
- data/.sublime-project +14 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +89 -41
- data/README.md +167 -9
- data/Rakefile +1 -1
- data/bin/check_lists +124 -22
- data/bin/export_csv +45 -36
- data/bin/export_hosts +34 -24
- data/bin/generate_categorised_lists +533 -0
- data/bin/generate_social_media_lists +398 -0
- data/bin/generate_video_lists +303 -124
- data/bin/generate_video_lists_focused +381 -0
- data/correct_usage_example.rb +18 -18
- data/lib/url_categorise/active_record_client.rb +11 -11
- data/lib/url_categorise/client.rb +856 -226
- data/lib/url_categorise/constants.rb +148 -70
- data/lib/url_categorise/dataset_processor.rb +68 -64
- data/lib/url_categorise/iab_compliance.rb +111 -99
- data/lib/url_categorise/models.rb +15 -15
- data/lib/url_categorise/version.rb +1 -1
- data/lib/url_categorise.rb +14 -14
- data/lists/blogging_domains.hosts +62 -0
- data/lists/crypto_domains.hosts +46 -0
- data/lists/developer_domains.hosts +108 -0
- data/lists/forum_domains.hosts +65 -0
- data/lists/gaming_domains.hosts +99 -0
- data/lists/messaging_domains.hosts +96 -0
- data/lists/music_domains.hosts +62 -0
- data/lists/security_domains.hosts +48 -0
- data/lists/social_media_domains.hosts +443 -0
- data/lists/streaming_domains.hosts +41 -0
- data/lists/video_hosting_domains.hosts +203 -175
- data/lists/video_url_patterns.txt +15 -15
- data/url_categorise.gemspec +11 -9
- metadata +69 -20
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
require 'time'
|
|
9
|
+
require 'set'
|
|
10
|
+
require 'optparse'
|
|
11
|
+
|
|
12
|
+
module SocialMediaListGenerator
|
|
13
|
+
class SiteParser
|
|
14
|
+
MR_HOLMES_API_URL = 'https://api.github.com/repos/Lucksi/Mr.Holmes/contents/Site_lists/Username'
|
|
15
|
+
MR_HOLMES_SITE_LIST_URL = 'https://raw.githubusercontent.com/Lucksi/Mr.Holmes/master/Site_lists/Username/site_list.json'
|
|
16
|
+
|
|
17
|
+
attr_accessor :max_ractor_workers, :batch_size, :parallel_threshold, :parallel_enabled
|
|
18
|
+
|
|
19
|
+
def initialize(options = {})
|
|
20
|
+
@social_media_domains = Set.new
|
|
21
|
+
@max_ractor_workers = options[:max_ractor_workers] || 4
|
|
22
|
+
@batch_size = options[:batch_size] || 25
|
|
23
|
+
@parallel_threshold = options[:parallel_threshold] || 10
|
|
24
|
+
@parallel_enabled = options.fetch(:parallel_enabled, true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def generate_lists
|
|
28
|
+
puts 'Generating social media domain lists...'
|
|
29
|
+
|
|
30
|
+
puts 'Adding manually curated social media domains...'
|
|
31
|
+
add_manual_domains
|
|
32
|
+
|
|
33
|
+
puts 'Fetching Mr.Holmes site list data...'
|
|
34
|
+
fetch_mr_holmes_data
|
|
35
|
+
|
|
36
|
+
create_lists_directory
|
|
37
|
+
generate_hosts_file
|
|
38
|
+
|
|
39
|
+
puts 'Social media lists generated successfully!'
|
|
40
|
+
puts 'Files created in lists/ directory:'
|
|
41
|
+
puts " - social_media_domains.hosts (#{@social_media_domains.size} domains)"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def add_manual_domains
|
|
47
|
+
# Core social media platforms - major networks
|
|
48
|
+
core_social = [
|
|
49
|
+
'facebook.com', 'www.facebook.com', 'm.facebook.com', 'web.facebook.com',
|
|
50
|
+
'instagram.com', 'www.instagram.com',
|
|
51
|
+
'twitter.com', 'www.twitter.com', 'mobile.twitter.com', 'x.com',
|
|
52
|
+
'threads.net', 'www.threads.net',
|
|
53
|
+
'tiktok.com', 'www.tiktok.com', 'vm.tiktok.com',
|
|
54
|
+
'linkedin.com', 'www.linkedin.com',
|
|
55
|
+
'pinterest.com', 'www.pinterest.com',
|
|
56
|
+
'reddit.com', 'www.reddit.com', 'old.reddit.com',
|
|
57
|
+
'tumblr.com', 'www.tumblr.com',
|
|
58
|
+
'snapchat.com', 'www.snapchat.com',
|
|
59
|
+
'whatsapp.com', 'www.whatsapp.com', 'web.whatsapp.com',
|
|
60
|
+
'telegram.org', 'www.telegram.org', 't.me', 'web.telegram.org',
|
|
61
|
+
'discord.com', 'www.discord.com', 'discord.gg',
|
|
62
|
+
'signal.org', 'www.signal.org',
|
|
63
|
+
'mastodon.social',
|
|
64
|
+
'bsky.app', 'bsky.social',
|
|
65
|
+
'vk.com', 'www.vk.com',
|
|
66
|
+
'weibo.com', 'www.weibo.com',
|
|
67
|
+
'wechat.com', 'www.wechat.com'
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
# Messaging and communication platforms
|
|
71
|
+
messaging = [
|
|
72
|
+
'messenger.com', 'www.messenger.com',
|
|
73
|
+
'slack.com', 'www.slack.com',
|
|
74
|
+
'kik.me',
|
|
75
|
+
'viber.com', 'www.viber.com',
|
|
76
|
+
'line.me', 'www.line.me',
|
|
77
|
+
'kakaotalk.com',
|
|
78
|
+
'community.signalusers.org'
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
# Content sharing and blogging
|
|
82
|
+
content_sharing = [
|
|
83
|
+
'medium.com',
|
|
84
|
+
'wordpress.com',
|
|
85
|
+
'blogspot.com',
|
|
86
|
+
'dev.to',
|
|
87
|
+
'substack.com',
|
|
88
|
+
'quora.com', 'www.quora.com',
|
|
89
|
+
'hubpages.com',
|
|
90
|
+
'flipboard.com',
|
|
91
|
+
'buzzfeed.com'
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
# Image and photo sharing
|
|
95
|
+
image_sharing = [
|
|
96
|
+
'imgur.com', 'www.imgur.com', 'i.imgur.com',
|
|
97
|
+
'flickr.com', 'www.flickr.com',
|
|
98
|
+
'deviantart.com', 'www.deviantart.com',
|
|
99
|
+
'unsplash.com',
|
|
100
|
+
'behance.net', 'www.behance.net',
|
|
101
|
+
'dribbble.com',
|
|
102
|
+
'vsco.co',
|
|
103
|
+
'500px.com',
|
|
104
|
+
'youpic.com',
|
|
105
|
+
'9gag.com'
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
# Professional and portfolio
|
|
109
|
+
professional = [
|
|
110
|
+
'about.me',
|
|
111
|
+
'linktr.ee',
|
|
112
|
+
'heylink.me',
|
|
113
|
+
'allmylinks.com',
|
|
114
|
+
'linkgenie.co',
|
|
115
|
+
'milkshake.app',
|
|
116
|
+
'ngl.link',
|
|
117
|
+
'ko-fi.com',
|
|
118
|
+
'patreon.com',
|
|
119
|
+
'buymeacoffee.com'
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
# Music and audio social
|
|
123
|
+
music_social = [
|
|
124
|
+
'soundcloud.com', 'www.soundcloud.com',
|
|
125
|
+
'bandcamp.com', 'www.bandcamp.com',
|
|
126
|
+
'mixcloud.com',
|
|
127
|
+
'audiojungle.net',
|
|
128
|
+
'rateyourmusic.com',
|
|
129
|
+
'smule.com', 'www.smule.com',
|
|
130
|
+
'ourdjtalk.com',
|
|
131
|
+
'house-mixes.com', 'www.house-mixes.com',
|
|
132
|
+
'freesound.org'
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
# Gaming social
|
|
136
|
+
gaming_social = [
|
|
137
|
+
'steamcommunity.com',
|
|
138
|
+
'twitch.tv', 'www.twitch.tv', 'clips.twitch.tv', 'm.twitch.tv',
|
|
139
|
+
'twitchtracker.com',
|
|
140
|
+
'roblox.com', 'www.roblox.com',
|
|
141
|
+
'fortnitetracker.com',
|
|
142
|
+
'psnprofiles.com',
|
|
143
|
+
'osu.ppy.sh',
|
|
144
|
+
'gamespot.com', 'www.gamespot.com',
|
|
145
|
+
'newgrounds.com',
|
|
146
|
+
'speedrun.com', 'www.speedrun.com',
|
|
147
|
+
'chess.com', 'www.chess.com',
|
|
148
|
+
'fandom.com', 'www.fandom.com',
|
|
149
|
+
'pokemonshowdown.com',
|
|
150
|
+
'splits.io',
|
|
151
|
+
'lolprofile.net',
|
|
152
|
+
'pubg.op.gg', 'euw.op.gg'
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
# Developer and tech social
|
|
156
|
+
developer_social = [
|
|
157
|
+
'github.com', 'www.github.com',
|
|
158
|
+
'gitlab.com',
|
|
159
|
+
'bitbucket.org',
|
|
160
|
+
'sourceforge.net',
|
|
161
|
+
'pypi.org',
|
|
162
|
+
'npmjs.com', 'www.npmjs.com',
|
|
163
|
+
'rubygems.org',
|
|
164
|
+
'hub.docker.com',
|
|
165
|
+
'gitee.com',
|
|
166
|
+
'notabug.org',
|
|
167
|
+
'scratch.mit.edu',
|
|
168
|
+
'codewars.com', 'www.codewars.com',
|
|
169
|
+
'codecademy.com', 'www.codecademy.com',
|
|
170
|
+
'hackaday.io',
|
|
171
|
+
'pastebin.com',
|
|
172
|
+
'asciinema.org',
|
|
173
|
+
'drupal.org', 'www.drupal.org',
|
|
174
|
+
'opensource.com',
|
|
175
|
+
'skillshare.com', 'www.skillshare.com'
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
# Security and hacking communities
|
|
179
|
+
security_social = [
|
|
180
|
+
'hackthebox.eu',
|
|
181
|
+
'tryhackme.com',
|
|
182
|
+
'hackerone.com',
|
|
183
|
+
'bugcrowd.com'
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
# Question and answer platforms
|
|
187
|
+
qa_platforms = [
|
|
188
|
+
'ask.fm',
|
|
189
|
+
'tellonym.me',
|
|
190
|
+
'disqus.com'
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
# Miscellaneous social platforms
|
|
194
|
+
misc_social = [
|
|
195
|
+
'myspace.com',
|
|
196
|
+
'ello.co',
|
|
197
|
+
'vbox7.com', 'www.vbox7.com',
|
|
198
|
+
'interpals.net', 'www.interpals.net',
|
|
199
|
+
'joinclubhouse.com',
|
|
200
|
+
'younow.com',
|
|
201
|
+
'camfrog.com',
|
|
202
|
+
'facciabuco.com', 'www.facciabuco.com',
|
|
203
|
+
'couchsurfing.com', 'www.couchsurfing.com',
|
|
204
|
+
'wattpad.com', 'www.wattpad.com',
|
|
205
|
+
'quotev.com', 'www.quotev.com',
|
|
206
|
+
'bookcrossing.com', 'www.bookcrossing.com',
|
|
207
|
+
'myanimelist.net',
|
|
208
|
+
'opensea.io',
|
|
209
|
+
'slideshare.net',
|
|
210
|
+
'openstreetmap.org', 'www.openstreetmap.org',
|
|
211
|
+
'gravatar.com',
|
|
212
|
+
'keybase.io',
|
|
213
|
+
'pscp.tv', 'www.pscp.tv',
|
|
214
|
+
'bitcoinforum.com',
|
|
215
|
+
'aminoapps.com',
|
|
216
|
+
'joinroll.com',
|
|
217
|
+
'pr0gramm.com',
|
|
218
|
+
'shitpostbot.com', 'www.shitpostbot.com',
|
|
219
|
+
'lookbook.nu',
|
|
220
|
+
'listal.com',
|
|
221
|
+
'career.habr.com',
|
|
222
|
+
'thehandbook.com', 'www.thehandbook.com',
|
|
223
|
+
'venmo.com',
|
|
224
|
+
'themeforest.net',
|
|
225
|
+
'cbtnuggets.com', 'www.cbtnuggets.com',
|
|
226
|
+
'7cups.com', 'www.7cups.com',
|
|
227
|
+
'quizsilo.com',
|
|
228
|
+
'colourlovers.com', 'www.colourlovers.com',
|
|
229
|
+
'21buttons.com', 'www.21buttons.com',
|
|
230
|
+
'passes.com',
|
|
231
|
+
'holopin.io'
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
# Website builders with social features
|
|
235
|
+
website_builders = [
|
|
236
|
+
'wix.com',
|
|
237
|
+
'jimdosite.com',
|
|
238
|
+
'mymonat.com'
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
# Nitter and alternative frontends
|
|
242
|
+
alternative_frontends = [
|
|
243
|
+
'nitter.net',
|
|
244
|
+
'urlebird.com',
|
|
245
|
+
'picuki.com', 'www.picuki.com',
|
|
246
|
+
'mstdn.io'
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
all_domains = core_social + messaging + content_sharing + image_sharing +
|
|
250
|
+
professional + music_social + gaming_social + developer_social +
|
|
251
|
+
security_social + qa_platforms + misc_social + website_builders +
|
|
252
|
+
alternative_frontends
|
|
253
|
+
|
|
254
|
+
all_domains.each { |domain| @social_media_domains.add(domain) }
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def fetch_mr_holmes_data
|
|
258
|
+
uri = URI(MR_HOLMES_SITE_LIST_URL)
|
|
259
|
+
response = Net::HTTP.get_response(uri)
|
|
260
|
+
|
|
261
|
+
unless response.code == '200'
|
|
262
|
+
puts ' Could not fetch from Mr.Holmes repository, using manual domains only'
|
|
263
|
+
return
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
begin
|
|
267
|
+
data = JSON.parse(response.body)
|
|
268
|
+
process_mr_holmes_site_list(data)
|
|
269
|
+
rescue JSON::ParserError => e
|
|
270
|
+
puts " Error parsing Mr.Holmes site list: #{e.message}"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
puts "\n Found #{@social_media_domains.size} unique social media domains"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def process_mr_holmes_site_list(data)
|
|
277
|
+
# The JSON structure is an array with a single hash containing all sites
|
|
278
|
+
sites = data.is_a?(Array) ? data.first : data
|
|
279
|
+
|
|
280
|
+
return unless sites.is_a?(Hash)
|
|
281
|
+
|
|
282
|
+
sites.each do |_key, site_info|
|
|
283
|
+
next unless site_info.is_a?(Hash)
|
|
284
|
+
|
|
285
|
+
main_domain = site_info['main']
|
|
286
|
+
next unless main_domain
|
|
287
|
+
|
|
288
|
+
# Clean domain (remove protocol and trailing slashes)
|
|
289
|
+
domain = clean_mr_holmes_domain(main_domain)
|
|
290
|
+
next unless domain && valid_domain?(domain)
|
|
291
|
+
|
|
292
|
+
@social_media_domains.add(domain.downcase)
|
|
293
|
+
|
|
294
|
+
# Extract additional domains from user URLs
|
|
295
|
+
extract_domains_from_url(site_info['user'])
|
|
296
|
+
extract_domains_from_url(site_info['user2'])
|
|
297
|
+
extract_domains_from_url(site_info['Scrapable_url']) if site_info['Scrapable_url']
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def clean_mr_holmes_domain(domain)
|
|
302
|
+
return nil unless domain
|
|
303
|
+
|
|
304
|
+
cleaned = domain.strip
|
|
305
|
+
cleaned = cleaned.sub(%r{^https?://}, '')
|
|
306
|
+
cleaned = cleaned.sub(%r{/+$}, '')
|
|
307
|
+
cleaned = cleaned.sub(/^www\./, '')
|
|
308
|
+
cleaned
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def extract_domains_from_url(url)
|
|
312
|
+
return unless url
|
|
313
|
+
|
|
314
|
+
match = url.match(%r{https?://(?:www\.)?([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})})
|
|
315
|
+
return unless match
|
|
316
|
+
|
|
317
|
+
domain = match[1].downcase
|
|
318
|
+
@social_media_domains.add(domain) if valid_domain?(domain)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def valid_domain?(domain)
|
|
322
|
+
return false unless domain
|
|
323
|
+
return false if domain.length < 4
|
|
324
|
+
return false unless domain.match?(/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)
|
|
325
|
+
return false if domain.include?('example.') || domain.include?('test.')
|
|
326
|
+
return false if domain.include?('localhost')
|
|
327
|
+
return false if domain.end_with?('.html') || domain.end_with?('.htm')
|
|
328
|
+
# Filter out API-only domains that aren't social media sites
|
|
329
|
+
return false if domain.start_with?('api.') && !domain.include?('mojang')
|
|
330
|
+
|
|
331
|
+
true
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def create_lists_directory
|
|
335
|
+
FileUtils.mkdir_p('lists')
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def generate_hosts_file
|
|
339
|
+
hosts_file_path = 'lists/social_media_domains.hosts'
|
|
340
|
+
|
|
341
|
+
File.open(hosts_file_path, 'w') do |file|
|
|
342
|
+
write_hosts_header(file)
|
|
343
|
+
|
|
344
|
+
@social_media_domains.sort.each do |domain|
|
|
345
|
+
file.puts "0.0.0.0 #{domain}"
|
|
346
|
+
file.puts "0.0.0.0 www.#{domain}" unless domain.start_with?('www.')
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def write_hosts_header(file)
|
|
352
|
+
file.puts '# Social Media Domains - PiHole Compatible Hosts File'
|
|
353
|
+
file.puts "# Generated on: #{Time.now.strftime('%Y-%m-%d %H:%M:%S UTC')}"
|
|
354
|
+
file.puts '# Sources:'
|
|
355
|
+
file.puts '# - Mr.Holmes OSINT tool (https://github.com/Lucksi/Mr.Holmes)'
|
|
356
|
+
file.puts '# - Manually curated social media platform domains'
|
|
357
|
+
file.puts '# Purpose: Identify and categorize social media websites'
|
|
358
|
+
file.puts '# Format: 0.0.0.0 domain.com'
|
|
359
|
+
file.puts '#'
|
|
360
|
+
file.puts '# This file contains domains extracted from the Mr.Holmes OSINT tool'
|
|
361
|
+
file.puts '# and manually curated lists to help identify and categorize'
|
|
362
|
+
file.puts '# social media platforms, messaging apps, and community websites.'
|
|
363
|
+
file.puts '#'
|
|
364
|
+
file.puts ''
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Run the generator if this file is executed directly
|
|
370
|
+
if __FILE__ == $0
|
|
371
|
+
# Default configuration
|
|
372
|
+
options = {
|
|
373
|
+
max_ractor_workers: 4,
|
|
374
|
+
batch_size: 25,
|
|
375
|
+
parallel_threshold: 10,
|
|
376
|
+
verbose: false
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
# Parse command-line options
|
|
380
|
+
OptionParser.new do |opts|
|
|
381
|
+
opts.banner = "Usage: #{$0} [options]"
|
|
382
|
+
opts.separator ''
|
|
383
|
+
opts.separator 'Generate social media domain lists from Mr.Holmes OSINT tool and curated sources'
|
|
384
|
+
opts.separator ''
|
|
385
|
+
|
|
386
|
+
opts.on('-v', '--verbose', 'Verbose output') do
|
|
387
|
+
options[:verbose] = true
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
opts.on('-h', '--help', 'Show this help message') do
|
|
391
|
+
puts opts
|
|
392
|
+
exit
|
|
393
|
+
end
|
|
394
|
+
end.parse!
|
|
395
|
+
|
|
396
|
+
generator = SocialMediaListGenerator::SiteParser.new(options)
|
|
397
|
+
generator.generate_lists
|
|
398
|
+
end
|