1 # == Schema Information
3 # Table name: social_links
5 # id :bigint not null, primary key
6 # user_id :bigint not null
8 # created_at :datetime not null
9 # updated_at :datetime not null
13 # index_social_links_on_user_id (user_id)
17 # fk_rails_... (user_id => users.id)
20 class SocialLink < ApplicationRecord
23 validates :url, :format => { :with => %r{\A(https?://.+|@([a-zA-Z0-9_]+)@([\w\-\.]+))\z}, :message => :http_parse_error }
26 :bluesky => %r{\Ahttps?://(?:www\.)?bsky\.app/profile/([a-zA-Z0-9\._-]+)},
27 :discord => %r{\Ahttps?://(?:www\.)?discord\.com/users/(\d+)},
28 :facebook => %r{\Ahttps?://(?:www\.)?facebook\.com/([a-zA-Z0-9.]+)},
29 :flickr => %r{\Ahttps?://(?:www\.)?flickr\.com/people/([a-zA-Z0-9@._-]+)},
30 :github => %r{\Ahttps?://(?:www\.)?github\.com/([a-zA-Z0-9_-]+)},
31 :gitlab => %r{\Ahttps?://(?:www\.)?gitlab\.com/([a-zA-Z0-9_-]+)},
32 :instagram => %r{\Ahttps?://(?:www\.)?instagram\.com/([a-zA-Z0-9._]+)},
33 :linkedin => %r{\Ahttps?://(?:www\.)?linkedin\.com/in/([a-zA-Z0-9_-]+)},
34 :line => %r{\Ahttps?://(?:www\.)?line\.me/ti/p/([a-zA-Z0-9_-]+)},
35 :mastodon => %r{\Ahttps?://(?:(?:www\.)?(mastodon\.social|en\.osm\.town))/(@[a-zA-Z0-9_]+)},
36 :mastodon_general => /\A@([a-zA-Z0-9_]+)@([\w\-\.]+)/,
37 :medium => %r{\Ahttps?://(?:www\.)?medium\.com/@([a-zA-Z0-9_]+)},
38 :ogf => %r{\Ahttps?://wiki\.opengeofiction\.net/index\.php/User:([a-zA-Z0-9_-]+)},
39 :ohm => %r{\Ahttps?://(?:www\.)?openhistoricalmap\.org/user/(\S+)},
40 :osm_forum => %r{\Ahttps?://community\.openstreetmap\.org/u/(\S+)},
41 :osm_wiki => %r{\Ahttps?://wiki\.openstreetmap\.org/wiki/User:([a-zA-Z0-9_-]+)},
42 :quora => %r{\Ahttps?://(?:www\.)?quora\.com/profile/([a-zA-Z0-9_-]+)},
43 :reddit => %r{\Ahttps?://(?:www\.)?reddit\.com/user/([a-zA-Z0-9_-]+)},
44 :slack => %r{\Ahttps?://join\.slack\.com/shareDM/([a-zA-Z0-9_~-]+)},
45 :snapchat => %r{\Ahttps?://(?:www\.)?snapchat\.com/add/([a-zA-Z0-9_-]+)},
46 :stackoverflow => %r{\Ahttps?://(?:www\.)?stackoverflow\.com/users/\d+/([a-zA-Z0-9_-]+)},
47 :strava => %r{\Ahttps?://(?:www\.)?strava\.com/athletes/([a-zA-Z0-9_-]+)},
48 :substack => %r{\Ahttps?://(?:www\.)?substack\.com/@([a-zA-Z0-9_-]+)},
49 :telegram => %r{\Ahttps?://(?:www\.)?t\.me/([a-zA-Z0-9_]+)},
50 :threads => %r{\Ahttps?://(?:www\.)?threads\.net/@([a-zA-Z0-9_]+)},
51 :tiktok => %r{\Ahttps?://(?:www\.)?tiktok\.com/@([a-zA-Z0-9_]+)},
52 :twitch => %r{\Ahttps?://(?:www\.)?twitch\.tv/([a-zA-Z0-9_]+)},
53 :twitter_x => %r{\Ahttps?://(?:www\.)?(?:twitter|x)\.com/([a-zA-Z0-9_]+)},
54 :vimeo => %r{\Ahttps?://(?:www\.)?vimeo\.com/([a-zA-Z0-9_]+)},
55 :whatsapp => %r{\Ahttps?://wa\.me/(\d+)},
56 :wikidata => %r{\Ahttps?://(?:www\.)?wikidata\.org/wiki/User:([a-zA-Z0-9_-]+)},
57 :wikimedia => %r{\Ahttps?://commons\.wikimedia\.org/wiki/User:([a-zA-Z0-9_-]+)},
58 :wikipedia => %r{\Ahttps?://(?:[a-zA-Z]+\.)?wikipedia\.org/wiki/User:([a-zA-Z0-9_-]+)},
59 :wikivoyage => %r{\Ahttps?://(?:[a-zA-Z]+\.)?wikivoyage\.org/wiki/User:([a-zA-Z0-9_-]+)},
60 :youtube => %r{\Ahttps?://(?:www\.)?youtube\.com/@([a-zA-Z0-9_-]+)}
63 NO_USERNAME_PLATFORMS = %w[discord line slack].freeze
66 URL_PATTERNS.each do |platform, pattern|
67 names = url.match(pattern)
70 if platform == :mastodon_general
72 :url => "https://#{names[2]}/@#{names[1]}",
73 :platform => "mastodon",
74 :name => "@#{names[1]}@#{names[2]}"
78 name = names[2].nil? ? names[1] : "#{names[2]}@#{names[1]}"
79 name = platform.to_s.capitalize if NO_USERNAME_PLATFORMS.include?(platform.to_s)
83 :platform => platform.to_s,
87 { :url => url, :platform => nil, :name => url.gsub(%r{https?://}, "") }