BT InLink number blacklist stats
Background
Across the UK, BT has units providing free phone calls, free Wi-Fi and two giant advertising screens called Street Hubs. I previously posted about the older variant of these (from when they were called InLink), but today I’m focusing on the phone call feature.
As you might guess, providing phone calls for free to anyone on the street can lead to abuse. So BT acted and introduced automatic blocking of suspicious numbers.
The blacklist
The Android tablets you use to make calls on these are filled with files called blacklist.json or blacklist-[digits].json files.
Unfortunately, the system is so locked down I couldn’t inspect these before and never knew the purpose. :(
But 3 days ago, I got the list! And I believe this is what Street Hubs use to check if they call a number.
Overview
Here’s the list, but without the phone numbers. It would be kind of silly to just drop those:
{
"name": "Default",
"black_list": [
{
"type": "NumberBlocks",
"start_time": "00:00",
"description": "Block specific mobiles on ALL sites",
"end_time": "23:59",
"day": "[0-6]",
"dialog": "CALL BLOCKED: This action was blocked. We restrict certain calls to protect our community if we believe it may be associated with unusual activity. Tell us if you think we made a mistake streethub@bt.com",
"site_id": ".*",
"number_patterns": [
"^(0|\\+44|0044)(a|lot|of|numbers)"
]
},
{
"type": "MobileBlocks",
"start_time": "00:00",
"description": "Block all mobile calls on SPECIFIC Sites, 24hr",
"end_time": "23:59",
"day": "[0-6]",
"dialog": "Sorry, calls to Mobile phones are restricted from this StreetHub",
"site_id": "(?i)Cuk-002385|uk-002387|uk-002412|uk-002445|uk-002404|uk-002489|uk-002473|uk-002480|uk-002511|uk-002499|uk-002495|uk-002501|uk-000013|uk-046904|uk-001130|uk-002518|uk-000025|uk-001134|uk-001202|uk-001511|uk-001335|uk-001310|uk-001391|uk-047647|uk-047083|uk-046849|uk-001361|uk-001512|uk-007601|uk-046868|uk-046882|uk-046883|uk-004529|uk-000050|uk-000053|uk-000037|uk-000042|uk-000039|uk-000058|uk-000038|uk-000049|UK-005915|uk-002383|uk-000014|uk-036778|uk-036775|uk-036912|uk-036774|uk-036871|uk-047519|uk-047515|uk-047523|uk-047526|uk-047518|uk-047521|uk-000034|uk-005242|uk-047570|uk-008551|uk-008549|uk-008696|uk-048391|uk-008703|uk-047869|uk568473|uk-036719|uk-047525|uk-651550|UK–713211|UK-710380|uk-023015|uk-694344|uk-790323|UK-589027|uk-105152|uk-043364|uk043364",
"number_patterns": [
"^(0|\\+44|0044)7[1-57-9][0-9]{8}"
]
},
{
"type": "MobileAndLandlineBlocks",
"start_time": "00:00",
"description": "Block all mobile and landline calls on SPECIFIC Sites, 24hr",
"end_time": "23:59",
"day": "[0-6]",
"dialog": "Sorry, calls to Mobile and Landline phones are restricted from this StreetHub",
"site_id": "(?i)bb-hudsonyards3|uk-505705|UK253076|UK276002|UK-686750|UK-253076|UK213304",
"number_patterns": [
"^(0|\\+44|0044)(1|2|7)[0-9]{8,9}"
]
}
],
"version": "2026-05-30T21:01:02.219215+01:00[Europe/London]"
}
Here we see that the rules system can:
- have multiple rules
- block numbers by regex
- time periods when rules are enforced
- the ability to target certain units.
The first rule is what I baited you to this blog post with. In this is a beautiful regex with 13,000+ phone numbers that no Street Hub can call.
The second blocks mobile calls completely for 77 units. I wish I could locate these…
The third rule blocks mobiles and landlines. Only 7 units are under this rule. These areas must have terribly worried councils to have to be blocked, lol.
What provider is blocked the most?
Content warning: bad workflow.
After wasting 3 or so hours to realise a shell loop with grep is not good for thousands of lines, I settled on Python and SQLite
to help answer this question. Basically, I use shell tools to transform the text a little, SQLite to store the data, and Python to query and count.
But before I touch either, I need to turn the regex into an actual list of numbers:
$ jq -r '.black_list[0].number_patterns[0][15:]' blacklist.json | tr '|()' '\n ' > blacklist.txt
Now we need a way to match phone numbers to providers. Ofcom provides numbering data with allocations and even dates.
The numbers in the blacklist start with 1, 2, 3, 7 and 8 so we’ll need the related CSV files. There’s a zip with all of them but I just downloaded them one by one, because, uh… yeah.
The CSV files look like this:
# this is s7.csv
NMS Number Block: Number Block,Block Status,CP Name,Non Geo Number Length,Allocation Date
7000 0,Allocated,Vodafone Limited,10 digit numbers,06/11/1995
7000 1,Allocated,Vodafone Limited,10 digit numbers,06/11/1995
The spacing in the numbers isn’t helpful here, so I used sed to remove it:
# i did this to all of them
$ sed -E 's/^([0-9]+) ([0-9]+)/\1\2/g' s7.csv | head -n5
NMS Number Block: Number Block,Block Status,CP Name,Non Geo Number Length,Allocation Date
70000,Allocated,Vodafone Limited,10 digit numbers,06/11/1995
70001,Allocated,Vodafone Limited,10 digit numbers,06/11/1995
Then I combined all the CSV files I needed into one, so I could import it into SQLite:
# make a header so it's nice to query in SQL
$ echo block,status,cp_name,number_length,alloc_date,notes >> full.csv
# now combine all the others, without their headers
$ tail -q -n+2 s*.csv >> full.csv
# sqlite import
$ sqlite3 numbers.db
SQLite version 3.47.2 2024-12-07 20:39:59
Enter ".help" for usage hints.
sqlite> .mode csv
sqlite> .import full.csv numbers
sqlite> .exit
Finally, the blacklist of phone numbers has to be turned into prefixes, otherwise the Python script or SQLite script will have to needlessly shorten numbers while running:
$ cut -c1-5 ../blacklist.txt | sort -u > prefixes.txt
The prefix list is also just shorter than the blacklist, at 3,944 lines compared to 13,338.
Then we can use the Python script, which is pretty simple:
import sqlite3
import json
with open("prefixes.txt") as f:
prefixes = f.read().split("\n")
counts = {}
db = sqlite3.connect("numbers.db")
for p in prefixes:
cur = db.execute("SELECT numbers.cp_name FROM numbers WHERE numbers.block = ?", (p,))
res = cur.fetchone() or ("None",)
if res[0] in counts:
counts[res[0]] += 1
else:
counts[res[0]] = 1
keys_sorted = sorted(counts, key=lambda key: counts[key], reverse=True)
for key in keys_sorted:
print(f"{counts[key]} {key}")
The script runs in under a minute, about 43 seconds. Here are the results!
1171 Telefonica UK Limited
965 Vodafone Limited
632 EE Limited ( TM)
500 Hutchison 3G UK Ltd
360 None
187 EE Limited (Orange)
81 Lycamobile (UK) Limited
20 Sky UK Limited
17 Virgin Mobile Telecoms Limited
6 Vectone Mobile Limited
2 Gamma Telecom Holdings Limited
2 Sure (Guernsey) Limited
1 Stour Marine Limited
1 JT (Jersey) Limited
“None” here just means nothing came up in the database. IT could very well have an allocation that I somehow missed.
Telefonica UK Limited, or what most people know as O2, is apparently the most blocked!
If you have other stats ideas, I would love to hear. Otherwise, thank you for reading my post! :D