<%= t(:policy_link) %>
+ +<%== format(Rack::Utils.escape_html(para), link: 'asxp.io/contact') %>
+ <% end %> + + <%== erb :_footer, layout: false %> +diff --git a/CLAUDE.md b/CLAUDE.md
index 1060ed0..3144525 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -53,6 +53,9 @@ renderings only for unbuilt sites) and credit format.
degraded, down (nothing answered for two buckets), unknown (worker stale,
or under half the controls answering). Not incidents: never cross out a
cell from this alone.
+- `/:locale/policy`: the no-unsolicited-messages policy, also in
+ `README.md`. Blocklists (SURBL) require a published one to delist; keep
+ both copies in step.
- `config.ru`: `www.` → `BINGO_HOST` 301.
- `views/`: `layout.erb` (CSS and SEO head), `index.erb` (card, provider
filter, dialogs), `dc.erb`, partials. `assets/`: icons.
diff --git a/README.md b/README.md
index 53a732e..b22e51c 100644
--- a/README.md
+++ b/README.md
@@ -22,4 +22,16 @@ of Claude (Anthropic). Part of the list came from the
Credit "Datacenter Bingo (datacenterbingo.xyz)"; the full attribution
line is in the file.
+## No unsolicited messages
+
+datacenterbingo.xyz is never advertised by unsolicited email, SMS,
+messenger, forum or social media messages. Neither we nor any third party
+may promote the site this way. The site sends no email and has no mailing
+list, newsletter or contact form, and it carries no ads, affiliate programs
+or paid promotion.
+
+If you received a message linking to this site, we did not send it. Please
+report it via [asxp.io/contact](https://asxp.io/contact). The same policy
+is on the site at [/en/policy](https://datacenterbingo.xyz/en/policy).
+
Development: `nix develop`, then `bin/test`. See `CLAUDE.md`.
diff --git a/app.rb b/app.rb
index 8afc537..70ccd60 100644
--- a/app.rb
+++ b/app.rb
@@ -269,7 +269,8 @@ class BingoWeb < Sinatra::Base
cache_control :public, max_age: 3600
dcs = Bingo::Datacenter.order(:position, :id).eager(:incidents).all
@entries = [[->(l) { "/#{l}" }, Bingo::Board.load.updated_on, '/'],
- [->(l) { "/#{l}/status" }, nil, '/en/status']] +
+ [->(l) { "/#{l}/status" }, nil, '/en/status'],
+ [->(l) { "/#{l}/policy" }, nil, '/en/policy']] +
dcs.map { |dc| [->(l) { dc_path(dc, l) }, [dc.updated_at, *dc.incidents.map(&:updated_at)].compact.max] }
erb :sitemap, layout: false
end
@@ -326,6 +327,20 @@ class BingoWeb < Sinatra::Base
erb :status
end
+ # Anti-spam policy; blocklists (SURBL) ask for a published one.
+ get %r{/(ru|en|uk)/policy} do |locale|
+ @locale = locale
+ @title = t(:policy_title)
+ @desc = t(:policy_desc)
+ @path_for = ->(l) { "/#{l}/policy" }
+ @x_default = '/en/policy'
+ url = "#{base_url}/#{locale}/policy"
+ @ld = JSON.generate('@context' => 'https://schema.org', '@type' => 'WebPage', '@id' => url, 'url' => url,
+ 'name' => @title, 'description' => @desc, 'inLanguage' => locale).gsub('', '<\/')
+ cache_control :public, max_age: 3600
+ erb :policy
+ end
+
get '/status.json' do
content_type 'application/json', charset: 'utf-8'
headers 'Access-Control-Allow-Origin' => '*'
diff --git a/lib/bingo.rb b/lib/bingo.rb
index 33fc3d5..635df03 100644
--- a/lib/bingo.rb
+++ b/lib/bingo.rb
@@ -86,7 +86,12 @@ module Bingo
status_cols: { 'dc' => 'ЦОД', 'state' => 'Сейчас', 'hours' => '24 часа', 'rtt' => 'мс', 'targets' => 'адр.' },
status_dc: 'Доступность сети', status_hour: '%{h} ч назад: %{p}',
status_desc: 'Доступность крупнейших дата-центров России онлайн: ежеминутные сетевые проверки IXcellerate, Selectel, DataPro, Key Point и других, история за 24 часа и открытые данные.',
- status_probes: 'Что проверяем', status_controls_list: 'Контрольные узлы в России', status_no_answer: 'не отвечает', status_source: 'почему этот адрес'
+ status_probes: 'Что проверяем', status_controls_list: 'Контрольные узлы в России', status_no_answer: 'не отвечает', status_source: 'почему этот адрес',
+ policy_link: 'Без спама', policy_title: 'Без нежелательных рассылок | Бинго ЦОДов',
+ policy_desc: 'Бинго ЦОДов никогда не рекламируется нежелательными сообщениями. Сайт не отправляет писем и не ведёт рассылок.',
+ policy_text: ['datacenterbingo.xyz никогда не рекламируется нежелательными письмами, SMS, сообщениями в мессенджерах, на форумах или в соцсетях. Ни мы, ни кто-либо другой не вправе продвигать сайт таким образом.',
+ 'Сайт не отправляет писем, у него нет рассылок, подписок и форм обратной связи. На нём нет рекламы, партнёрских программ и платного продвижения.',
+ 'Если вы получили сообщение со ссылкой на этот сайт, его отправили не мы. Пожалуйста, сообщите о нём через %{link}.']
},
'en' => {
seo_title: 'Datacenter Bingo — strikes, fires and outages at Russian datacenters',
@@ -132,7 +137,12 @@ module Bingo
status_cols: { 'dc' => 'Datacenter', 'state' => 'Now', 'hours' => '24 hours', 'rtt' => 'ms', 'targets' => 'addr.' },
status_dc: 'Network reachability', status_hour: '%{h} h ago: %{p}',
status_desc: "Live reachability of Russia's largest datacenters: minute-by-minute network checks of IXcellerate, Selectel, DataPro, Key Point and others, 24-hour history and open data.",
- status_probes: 'What we check', status_controls_list: 'Control hosts in Russia', status_no_answer: 'no answer', status_source: 'why this address'
+ status_probes: 'What we check', status_controls_list: 'Control hosts in Russia', status_no_answer: 'no answer', status_source: 'why this address',
+ policy_link: 'No-spam policy', policy_title: 'No unsolicited messages | Datacenter Bingo',
+ policy_desc: 'Datacenter Bingo is never advertised by unsolicited messages. The site sends no email and runs no mailing list.',
+ policy_text: ['datacenterbingo.xyz is never advertised by unsolicited email, SMS, messenger, forum or social media messages. Neither we nor any third party may promote the site this way.',
+ 'The site sends no email and has no mailing list, newsletter or contact form. It carries no ads, affiliate programs or paid promotion.',
+ 'If you received a message linking to this site, we did not send it. Please report it via %{link}.']
},
'uk' => {
seo_title: 'Бінго ЦОДів — атаки, пожежі та аварії дата-центрів Росії',
@@ -178,7 +188,12 @@ module Bingo
status_cols: { 'dc' => 'ЦОД', 'state' => 'Зараз', 'hours' => '24 години', 'rtt' => 'мс', 'targets' => 'адр.' },
status_dc: 'Доступність мережі', status_hour: '%{h} год тому: %{p}',
status_desc: 'Доступність найбільших дата-центрів Росії онлайн: щохвилинні мережеві перевірки IXcellerate, Selectel, DataPro, Key Point та інших, історія за 24 години та відкриті дані.',
- status_probes: 'Що перевіряємо', status_controls_list: 'Контрольні вузли в Росії', status_no_answer: 'не відповідає', status_source: 'чому ця адреса'
+ status_probes: 'Що перевіряємо', status_controls_list: 'Контрольні вузли в Росії', status_no_answer: 'не відповідає', status_source: 'чому ця адреса',
+ policy_link: 'Без спаму', policy_title: 'Без небажаних розсилок | Бінго ЦОДів',
+ policy_desc: 'Бінго ЦОДів ніколи не рекламується небажаними повідомленнями. Сайт не надсилає листів і не веде розсилок.',
+ policy_text: ['datacenterbingo.xyz ніколи не рекламується небажаними листами, SMS, повідомленнями в месенджерах, на форумах чи в соцмережах. Ні ми, ні будь-хто інший не має права просувати сайт у такий спосіб.',
+ 'Сайт не надсилає листів, не має розсилок, підписок і форм зворотного зв’язку. На ньому немає реклами, партнерських програм і платного просування.',
+ 'Якщо ви отримали повідомлення з посиланням на цей сайт, його надіслали не ми. Будь ласка, повідомте про нього через %{link}.']
}
}.freeze
diff --git a/test/bingo_test.rb b/test/bingo_test.rb
index f3cf031..b6cc5a9 100644
--- a/test/bingo_test.rb
+++ b/test/bingo_test.rb
@@ -150,6 +150,17 @@ class BingoTest < Minitest::Test
assert_includes last_response.body, 'hackyourmom'
end
+ def test_policy_page_in_every_locale_and_footer
+ { 'en' => 'never advertised', 'ru' => 'никогда не рекламируется', 'uk' => 'ніколи не рекламується' }.each do |l, text|
+ get "/#{l}/policy"
+ assert last_response.ok?
+ assert_includes last_response.body, text
+ assert_includes last_response.body, 'asxp.io/contact'
+ end
+ get '/en'
+ assert_includes last_response.body, 'No-spam policy'
+ end
+
def test_robots_and_sitemap_are_the_bingos_own
get '/robots.txt'
assert last_response.ok?
@@ -161,7 +172,8 @@ class BingoTest < Minitest::Test
%w[ru en uk].each { |l| assert_includes last_response.body, "
<%= t(:credits_ai) %> <%== format(Rack::Utils.escape_html(t(:credits_hym)), link: 'hackyourmom') %>
-RSS · JSON Feed · <%= t(:open_data) %> · <%= t(:status_link) %>
+RSS · JSON Feed · <%= t(:open_data) %> · <%= t(:status_link) %> · <%= t(:policy_link) %>
© <%= Date.today.year %> asxp.io · <%= t(:source_code) %>
diff --git a/views/policy.erb b/views/policy.erb new file mode 100644 index 0000000..193f9c2 --- /dev/null +++ b/views/policy.erb @@ -0,0 +1,17 @@ +<%== format(Rack::Utils.escape_html(para), link: 'asxp.io/contact') %>
+ <% end %> + + <%== erb :_footer, layout: false %> +