from requests import get
from json import loads
from re import sub
+from datetime import datetime
with open("config.json") as f:
config = loads(f.read())
"blocks_recorded": blocks
}
+@app.get(base_url+"/top")
+def top(blocked: int = None, blockers: int = None):
+ conn = sqlite3.connect("blocks.db")
+ c = conn.cursor()
+ if blocked == None and blockers == None:
+ raise HTTPException(status_code=400, detail="No filter specified")
+ elif blocked != None:
+ if blocked > 500:
+ raise HTTPException(status_code=400, detail="Too many results")
+ c.execute("select blocked, count(blocked) from blocks where block_level = 'reject' group by blocked order by count(blocked) desc limit ?", (blocked,))
+ elif blockers != None:
+ if blockers > 500:
+ raise HTTPException(status_code=400, detail="Too many results")
+ c.execute("select blocker, count(blocker) from blocks where block_level = 'reject' group by blocker order by count(blocker) desc limit ?", (blockers,))
+ scores = c.fetchall()
+ c.close()
+
+ scoreboard = []
+ print(scores)
+ for domain, highscore in scores:
+ scoreboard.append({"domain": domain, "highscore": highscore})
+
+ return scoreboard
+
@app.get(base_url+"/api")
-def blocked(domain: str = None, reason: str = None):
- if domain == None and reason == None:
+def blocked(domain: str = None, reason: str = None, reverse: str = None):
+ if domain == None and reason == None and reverse == None:
raise HTTPException(status_code=400, detail="No filter specified")
if reason != None:
reason = sub("(%|_)", "", reason)
if domain != None:
wildchar = "*." + ".".join(domain.split(".")[-domain.count("."):])
punycode = domain.encode('idna').decode('utf-8')
- c.execute("select blocker, blocked, block_level, reason from blocks where blocked = ? or blocked = ? or blocked = ? or blocked = ? or blocked = ? or blocked = ?",
+ c.execute("select blocker, blocked, block_level, reason, first_added, last_seen from blocks where blocked = ? or blocked = ? or blocked = ? or blocked = ? or blocked = ? or blocked = ? order by first_added asc",
(domain, "*." + domain, wildchar, get_hash(domain), punycode, "*." + punycode))
+ elif reverse != None:
+ c.execute("select blocker, blocked, block_level, reason, first_added, last_seen from blocks where blocker = ? order by first_added asc", (reverse,))
else:
- c.execute("select blocker, blocked, reason, block_level from blocks where reason like ? and reason != ''", ("%"+reason+"%",))
+ c.execute("select blocker, blocked, block_level, reason, first_added, last_seen from blocks where reason like ? and reason != '' order by first_added asc", ("%"+reason+"%",))
blocks = c.fetchall()
- conn.close()
+ c.close()
result = {}
- reasons = {}
- wildcards = []
- if domain != None:
- for domain, blocked, block_level, reason in blocks:
- if block_level in result:
- result[block_level].append(domain)
- else:
- result[block_level] = [domain]
- if blocked == "*." + ".".join(blocked.split(".")[-blocked.count("."):]):
- wildcards.append(domain)
- if reason != "":
- if block_level in reasons:
- reasons[block_level][domain] = reason
- else:
- reasons[block_level] = {domain: reason}
- return {"blocks": result, "reasons": reasons, "wildcards": wildcards}
-
- for blocker, blocked, reason, block_level in blocks:
+ for blocker, blocked, block_level, reason, first_added, last_seen in blocks:
+ entry = {"blocker": blocker, "blocked": blocked, "reason": reason, "first_added": first_added, "last_seen": last_seen}
if block_level in result:
- result[block_level].append({"blocker": blocker, "blocked": blocked, "reason": reason})
+ result[block_level].append(entry)
else:
- result[block_level] = [{"blocker": blocker, "blocked": blocked, "reason": reason}]
- return {"blocks": result}
+ result[block_level] = [entry]
+
+ return result
+
+@app.get(base_url+"/scoreboard")
+def index(request: Request, blockers: int = None, blocked: int = None):
+ if blockers == None and blocked == None:
+ raise HTTPException(status_code=400, detail="No filter specified")
+ elif blockers != None:
+ scores = get(f"http://127.0.0.1:{port}{base_url}/top?blockers={blockers}")
+ elif blocked != None:
+ scores = get(f"http://127.0.0.1:{port}{base_url}/top?blocked={blocked}")
+ if scores != None:
+ if not scores.ok:
+ raise HTTPException(status_code=blocks.status_code, detail=blocks.text)
+ scores = scores.json()
+ return templates.TemplateResponse("index.html", {"request": request, "scoreboard": True, "blockers": blockers, "blocked": blocked, "scores": scores})
@app.get(base_url+"/")
-def index(request: Request, domain: str = None, reason: str = None):
- if domain == "" or reason == "":
+def index(request: Request, domain: str = None, reason: str = None, reverse: str = None):
+ if domain == "" or reason == "" or reverse == "":
return responses.RedirectResponse("/")
info = None
blocks = None
- if domain == None and reason == None:
+ if domain == None and reason == None and reverse == None:
info = get(f"http://127.0.0.1:{port}{base_url}/info")
if not info.ok:
raise HTTPException(status_code=info.status_code, detail=info.text)
blocks = get(f"http://127.0.0.1:{port}{base_url}/api?domain={domain}")
elif reason != None:
blocks = get(f"http://127.0.0.1:{port}{base_url}/api?reason={reason}")
+ elif reverse != None:
+ blocks = get(f"http://127.0.0.1:{port}{base_url}/api?reverse={reverse}")
if blocks != None:
if not blocks.ok:
raise HTTPException(status_code=blocks.status_code, detail=blocks.text)
blocks = blocks.json()
- return templates.TemplateResponse("index.html", {"request": request, "domain": domain, "blocks": blocks, "reason": reason, "info": info})
+ for block_level in blocks:
+ for block in blocks[block_level]:
+ block["first_added"] = datetime.utcfromtimestamp(block["first_added"]).strftime('%Y-%m-%d %H:%M')
+ block["last_seen"] = datetime.utcfromtimestamp(block["last_seen"]).strftime('%Y-%m-%d %H:%M')
+
+ return templates.TemplateResponse("index.html", {"request": request, "domain": domain, "blocks": blocks, "reason": reason, "reverse": reverse, "info": info})
if __name__ == "__main__":
uvicorn.run("api:app", host="127.0.0.1", port=port, log_level="info")
}
.block_level {
background-color: #1c1c3c;
- width: 750px;
+ width: 80em;
padding: 5px;
margin: auto;
margin-top: 10px;
}
+ .scoreboard {
+ background-color: #1c1c3c;
+ width: 40em;
+ padding: 5px;
+ margin: auto;
+ margin-top: 10px;
+ }
+ table {
+ width: 100%;
+ background-color: #2d2d4d;
+ border-spacing: 0px;
+ }
+ table tr:nth-of-type(2n) {
+ background-color: #1c1c3c;
+ }
+ table td {
+ padding: 4px;
+ }
+ .block_level table td:nth-of-type(1), .block_level table td:nth-of-type(2),
+ .block_level table td:nth-of-type(4), .block_level table td:nth-of-type(5) {
+ white-space: nowrap;
+ }
.block {
background-color: #2d2d4d;
padding: 5px;
</style>
</head>
<body>
- {% if reason %}
- <h1>Instances that use "{{reason}}" in their Reason</h1>
- {% for block_level in blocks.blocks %}
- <div class="block_level">
- <h2>{{block_level}} ({{blocks.blocks[block_level]|length}})</h2>
- {% for block in blocks.blocks[block_level] %}
- <div class="block">
- <img src="https://proxy.duckduckgo.com/ip3/{{block.blocker}}.ico" width=16/>
- <b><a href="https://{{block.blocker}}" rel="nofollow noopener noreferrer">{{block.blocker}}</a></b> ->
- <img src="https://proxy.duckduckgo.com/ip3/{{block.blocked}}.ico" width=16/>
- <b><a href="https://{{block.blocked}}" rel="nofollow noopener noreferrer">{{block.blocked}}</a></b><br/>
- {{block.reason}}
- </div>
+ {% if scoreboard %}
+ {% if blockers %}
+ <h1>Top {{blockers}} defederating instances</h1>
+ {% elif blocked %}
+ <h1>Top {{blocked}} defederated instances</h1>
+ {% endif %}
+ <div class="scoreboard">
+ <table>
+ <th>Instance</th>
+ <th>Defederations</th>
+ {% for entry in scores %}
+ <tr>
+ <td>
+ <img src="https://proxy.duckduckgo.com/ip3/{{entry['domain']}}.ico" width=16/>
+ <b><a href="https://{{entry['domain']}}" rel="nofollow noopener noreferrer">{{entry['domain']}}</a></b>
+ </td>
+ <td>{{entry['highscore']}}</td>
+ </tr>
{% endfor %}
- </div>
- {% endfor %}
- {% elif blocks %}
- <h1>Instances that block {{domain}}</h1>
- {% for block_level in blocks.blocks %}
+ </table>
+ </div>
+ {% elif reason or blocks or reverse %}
+ {% if reason %}
+ <h1>Instances that use "{{reason}}" in their reason</h1>
+ {% elif reverse %}
+ <h1>Instances that are blocked by {{reverse}}</h1>
+ {% elif blocks %}
+ <h1>Instances that block {{domain}}</h1>
+ {% endif %}
+ {% for block_level in blocks %}
<div class="block_level" id="{{block_level}}">
- <h2>{{block_level}} ({{blocks.blocks[block_level]|length}})</h2>
- {% for block in blocks.blocks[block_level] %}
- <div class="block">
- <img src="https://proxy.duckduckgo.com/ip3/{{block}}.ico" width=16/>
- <b><a href="https://{{block}}" rel="nofollow noopener noreferrer">{{block}}</a></b>
- {% if block in blocks.wildcards %}
- (<span title="wildcard block">∗</span>)
- {% endif %}
- <br/>
- {% if block_level in blocks.reasons %}
- {{blocks.reasons[block_level][block]}}
- {% endif %}
- </div>
- {% endfor %}
+ <h2>{{block_level}} ({{blocks[block_level]|length}})</h2>
+ <table>
+ <th>Blocker</th>
+ <th>{% if block_level == 'accept' %}Accepted{% else %}Blocked{% endif %}</th>
+ <th>Reason</th>
+ <th>First added</th>
+ <th>Last seen</th>
+ {% for block in blocks[block_level] %}
+ <tr>
+ <td>
+ <img src="https://proxy.duckduckgo.com/ip3/{{block['blocker']}}.ico" width=16/>
+ <b><a href="https://{{block['blocker']}}" rel="nofollow noopener noreferrer">{{block['blocker']}}</a></b>
+ </td>
+ <td>
+ <img src="https://proxy.duckduckgo.com/ip3/{{domain or block['blocked']}}.ico" width=16/>
+ <b><a href="https://{{domain or block['blocked']}}" rel="nofollow noopener noreferrer">{{block['blocked']}}</a></b>
+ </td>
+ <td>{{block['reason']}}</td>
+ <td>{{block['first_added']}}</td>
+ <td>{{block['last_seen']}}</td>
+ </tr>
+ {% endfor %}
+ </table>
</div>
{% endfor %}
{% else %}
<input type="text" name="reason" placeholder="free speech" />
<input type="submit" value="Submit" />
</form>
+ <h1>Reverse search</h1>
+ <form>
+ <input type="text" name="reverse" placeholder="example.com" />
+ <input type="submit" value="Submit" />
+ </form>
+ <p>
+ <a href="./scoreboard?blockers=50">top 50 defederating</a> / <a href="./scoreboard?blocked=50">defederated instances</a>
+ </p>
<div class="info">
known instances: {{info.known_instances}}<br/>
indexed instances: {{info.indexed_instances}}<br/>
blocks recorded: {{info.blocks_recorded}}<br/>
- source code: <a href="https://git.kiwifarms.net/mint/fedi-block-api">git.kiwifarms.net/mint/fedi-block-api</a> (<a href="https://gitgud.io/mintplg/fedi-block-api">mirror</a>)
+ source code: <a href="https://git.kiwifarms.net/mint/fedi-block-api">git.kiwifarms.net/mint/fedi-block-api</a> (<a href="https://gitgud.io/mintplg/fedi-block-api">mirror</a>)<br/>
+ fuck jannies
</div>
{% endif %}
</body>