commit
f7c044ef79
@ -0,0 +1,63 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
@ -0,0 +1,342 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
appsettings.json
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
/BantFlags/wwwroot/flags
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- Backup*.rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "3.1.0",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
@page
|
||||
@model BantFlags.Pages.IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "BantFlags";
|
||||
Layout = "~/Pages/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
<h1>/bant/ Flags</h1>
|
||||
<p>E<span lang="jp">メール</span>: boku (at) plum (dot) moe</p>
|
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"]</title>
|
||||
@RenderSection("Head", required: false)
|
||||
</head>
|
||||
<body style="text-align: center;">
|
||||
@RenderBody()
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BantFlags
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
// TODO: set this up properly for use on Debian.
|
||||
webBuilder.ConfigureLogging(logBuilder =>
|
||||
logBuilder.AddFile(@"/var/log/bantflags.log", minimumLevel: LogLevel.Information));
|
||||
webBuilder.UseStartup<Startup>();
|
||||
})
|
||||
.ConfigureAppConfiguration((host, config) =>
|
||||
{
|
||||
// Explicitly look for appsettings.json in the program's directory
|
||||
config.AddJsonFile(Path.Join(AppDomain.CurrentDomain.BaseDirectory + "appsettings.json"), optional: false, reloadOnChange: false);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
using BantFlags.Data;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace BantFlags
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddControllers()
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
services.AddRazorPages();
|
||||
|
||||
services.AddSingleton(new DatabaseService(Configuration.GetSection("dbconfig").Get<DatabaseServiceConfig>()));
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
string webroot = Configuration.GetValue<string>("webroot");
|
||||
if (webroot != null)
|
||||
{
|
||||
env.WebRootPath = webroot;
|
||||
env.WebRootFileProvider = new PhysicalFileProvider(env.WebRootPath);
|
||||
}
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
{
|
||||
ServeUnknownFileTypes = true,
|
||||
DefaultContentType = "text/plain"
|
||||
});
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapRazorPages();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"AllowedHosts": "*",
|
||||
"dbconfig": {
|
||||
"connectionstring": "Server=localhost;Port=3306;User ID=user;Password=default;Database=bantflags",
|
||||
"poolsize": 2
|
||||
},
|
||||
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
|
||||
"webroot": ""
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
// ==UserScript==
|
||||
// @name BantFlags
|
||||
// @namespace BintFlegs
|
||||
// @description More flags for r/banter
|
||||
// @include http*://boards.4chan.org/bant/*
|
||||
// @include http*://archive.nyafuu.org/bant/*
|
||||
// @include http*://archived.moe/bant/*
|
||||
// @include http*://thebarchive.com/bant/*
|
||||
// @exclude http*://boards.4chan.org/bant/catalog
|
||||
// @exclude http*://archive.nyafuu.org/bant/statistics/
|
||||
// @exclude http*://archived.moe/bant/statistics/
|
||||
// @exclude http*://thebarchive.com/bant/statistics/
|
||||
// @version 0.8.0
|
||||
// @grant GM_xmlhttpRequest
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @run-at document-end
|
||||
// @icon https://nineball.party/files/flags/actual_flags/0077.png
|
||||
// @updateURL https://flags.plum.moe/bantflags.meta.js
|
||||
// @downloadURL https://flags.plum.moe/bantflags.user.js
|
||||
// ==/UserScript==
|
@ -0,0 +1,421 @@
|
||||
// ==UserScript==
|
||||
// @name BantFlags
|
||||
// @namespace BintFlegs
|
||||
// @description More flags for r/banter
|
||||
// @include http*://boards.4chan.org/bant/*
|
||||
// @include http*://archive.nyafuu.org/bant/*
|
||||
// @include http*://archived.moe/bant/*
|
||||
// @include http*://thebarchive.com/bant/*
|
||||
// @exclude http*://boards.4chan.org/bant/catalog
|
||||
// @exclude http*://archive.nyafuu.org/bant/statistics/
|
||||
// @exclude http*://archived.moe/bant/statistics/
|
||||
// @exclude http*://thebarchive.com/bant/statistics/
|
||||
// @version 0.8.0
|
||||
// @grant GM_xmlhttpRequest
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @run-at document-end
|
||||
// @icon https://nineball.party/files/flags/actual_flags/0077.png
|
||||
// @updateURL https://flags.plum.moe/bantflags.meta.js
|
||||
// @downloadURL https://flags.plum.moe/bantflags.user.js
|
||||
// ==/UserScript==
|
||||
|
||||
// This script specifically targets ECMAScript 2015 (const, let, arrow functions). Update your hecking browser.
|
||||
|
||||
// Change this if you want verbose debug information in the console.
|
||||
const debugMode = true;
|
||||
|
||||
//
|
||||
// DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR FLAGS SHOULD BE CONFIGURED USING THE CONFIGURATION BOXES
|
||||
//
|
||||
const postRemoveCounter = 60;
|
||||
const requestRetryInterval = 5000; // TODO: maybe a max retries counter?
|
||||
const regionVariable = 'regionVariableAPI2'; // TODO: This is where GM stores flags permanantly. We could use a better name.
|
||||
const regionDivider = "||"; //TODO: We can probably remove this and seperate by ,
|
||||
const is_archive = window.location.host !== "boards.4chan.org";
|
||||
const boardID = "bant"; //TODO: Hardcode /bant/ or accept other boards.
|
||||
const version = 1; // Breaking changes.
|
||||
const back_end = 'https://flags.plum.moe/';
|
||||
const api_flags = 'api/flags';
|
||||
const flag_dir = 'flags/';
|
||||
const api_get = 'api/get';
|
||||
const api_post = 'api/post';
|
||||
|
||||
var regions = []; // The flags we have selected.
|
||||
var postNrs = []; // all post numbers in the thread.
|
||||
|
||||
//
|
||||
// DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR FLAGS SHOULD BE CONFIGURED USING THE CONFIGURATION BOXES
|
||||
//
|
||||
|
||||
let elementsInClass = x => document.getElementsByClassName(x);
|
||||
let sliceCall = x => Array.prototype.slice.call(x);
|
||||
let firstChildInClass = (parent, className) => parent.getElementsByClassName(className)[0];
|
||||
let createAndAssign = (element, source) => Object.assign(document.createElement(element), source);
|
||||
|
||||
function addGlobalStyle(css) {
|
||||
let head = document.getElementsByTagName('head')[0];
|
||||
if (!head) {
|
||||
console.error('[BantFlags] No head tag??');
|
||||
return;
|
||||
}
|
||||
|
||||
head.appendChild(createAndAssign('style', {
|
||||
type: 'text/css',
|
||||
innerHTML: css
|
||||
}));
|
||||
}
|
||||
|
||||
function debug(text) {
|
||||
if (debugMode) {
|
||||
console.log("[BantFlags] " + text);
|
||||
}
|
||||
}
|
||||
|
||||
/** Wrapper around GM_xmlhttpRequest
|
||||
* @param {string} method - The HTTP method (GET, POST)
|
||||
* @param {string} url - The URL of the request
|
||||
* @param {string} data - Data sent inn the form body
|
||||
* @param {Function} func - The function run after onload. Response data is sent directly to it. */
|
||||
function MakeRequest(method, url, data, func) {
|
||||
GM_xmlhttpRequest({
|
||||
method: method,
|
||||
url: url,
|
||||
data: data,
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
onload: func
|
||||
});
|
||||
}
|
||||
|
||||
function retry(func, resp) {
|
||||
console.log("[BantFlags] Could not fetch flags, status: " + resp.status);
|
||||
console.log(resp.statusText); // TODO: surely ASP.NET can return something more useful?
|
||||
setTimeout(func, requestRetryInterval);
|
||||
}
|
||||
|
||||
/** nSetup, preferences */
|
||||
|
||||
// TODO: this shouldn't be a class.
|
||||
var nsetup = { // not anymore a clone of the original setup
|
||||
namespace: 'BintFlegs', // TODO: should be const.
|
||||
flagsLoaded: false,
|
||||
form: "<span id=\"bantflags_container\"></span>" +
|
||||
"<button type=\"button\" id=\"append_flag_button\" title=\"Click to add selected flag to your flags. Click on flags to remove them. Saving happens automatically, you only need to refresh the pages that have an outdated flaglist on the page.\"><<</button>" +
|
||||
"<button id=\"flagLoad\" type=\"button\">Click to load flags.</button><select id=\"flagSelect\"></select>",
|
||||
fillHtml: function () { // TODO: this function should have a better name. Only called by nsetup.init, can be inlined?
|
||||
|
||||
// resolve flags
|
||||
MakeRequest(
|
||||
"GET",
|
||||
back_end + api_flags,
|
||||
"", // Because we're GETting.
|
||||
function (resp) {
|
||||
debug('Loading flags');
|
||||
if (resp.status !== 200) {
|
||||
retry(nsetup.fillHtml, resp);
|
||||
return;
|
||||
}
|
||||
|
||||
let flagSelect = document.getElementById("flagSelect");
|
||||
let flagLoad = document.getElementById('flagLoad');
|
||||
let x = resp.responseText.split('\n');
|
||||
|
||||
for (var i = 0; i < x.length; i++) {
|
||||
let flag = x[i];
|
||||
flagSelect.appendChild(createAndAssign('option', {
|
||||
value: flag,
|
||||
innerHTML: "<img src=\"" + back_end + flag_dir + flag + ".png\"" + " title=\"" + flag + "\">" + " " + flag
|
||||
}));
|
||||
}
|
||||
|
||||
flagLoad.style.display = 'none';
|
||||
flagSelect.style.display = 'inline-block';
|
||||
nsetup.flagsLoaded = true;
|
||||
flagLoad.removeEventListener('click', nsetup.fillHtml);
|
||||
});
|
||||
},
|
||||
save: function (k, v) {
|
||||
GM_setValue(nsetup.namespace + k, v);
|
||||
regions = nsetup.load(regionVariable);
|
||||
},
|
||||
load: k => GM_getValue(nsetup.namespace + k), // We can get rid of this and just pass regionVariable to GM_getvalue in the two places we need it.
|
||||
setFlag: function (flag) { // place a flag from the selector to the flags array variable and create an element in the flags_container div
|
||||
let UID = Math.random().toString(36).substring(7);
|
||||
let flagName = flag ? flag : document.getElementById("flagSelect").value;
|
||||
let flagContainer = document.getElementById("bantflags_container");
|
||||
|
||||
flagContainer.appendChild(createAndAssign('img', {
|
||||
title: flagName,
|
||||
src: back_end + flag_dir + flagName + ".png",
|
||||
id: UID,
|
||||
className: 'bantflags_flag'
|
||||
}));
|
||||
|
||||
let flagsCount = flagContainer.children.length;
|
||||
if (flagsCount > 8) {// TODO: set to constant and enforce server side.
|
||||
nsetup.gray("on");
|
||||
} // Why does 8 work? What happened to the async issue a moment ago?
|
||||
|
||||
document.getElementById(UID).addEventListener("click", function () {
|
||||
let flagToRemove = document.getElementById(UID);
|
||||
|
||||
flagToRemove.parentNode.removeChild(flagToRemove);
|
||||
nsetup.gray("off");
|
||||
nsetup.save(regionVariable, nsetup.parse());
|
||||
});
|
||||
|
||||
if (!flag) {
|
||||
nsetup.save(regionVariable, nsetup.parse());
|
||||
}
|
||||
},
|
||||
|
||||
init: function () {
|
||||
|
||||
// here we insert the form for placing flags. How?
|
||||
let flagsForm = createAndAssign("div", {
|
||||
className: 'flagsForm',
|
||||
innerHTML: nsetup.form
|
||||
});
|
||||
|
||||
addGlobalStyle('.flagsForm{float: right; clear: right; margin: 20px 10px;} #flagSelect{display:none;}');
|
||||
addGlobalStyle(".bantflags_flag { padding: 1px;} [title^='Romania'] { position: relative; animation: shakeAnim 0.1s linear infinite;} @keyframes shakeAnim { 0% {left: 1px;} 25% {top: 2px;} 50% {left: 1px;} 75% {left: 0px;} 100% {left: 2px;}}");
|
||||
|
||||
firstChildInClass(document, 'bottomCtrl').parentNode.appendChild(flagsForm);
|
||||
|
||||
for (var i in regions) {
|
||||
nsetup.setFlag(regions[i]);
|
||||
}
|
||||
|
||||
document.getElementById("append_flag_button").addEventListener("click", (e) => {
|
||||
if (nsetup.flagsLoaded) {
|
||||
nsetup.setFlag();
|
||||
}
|
||||
else {
|
||||
alert('Load flags before adding them.');
|
||||
}
|
||||
});
|
||||
document.getElementById('flagLoad').addEventListener('click', nsetup.fillHtml);
|
||||
},
|
||||
parse: function () {
|
||||
let flagsArray = [];
|
||||
let flagElements = elementsInClass("bantflags_flag");
|
||||
|
||||
for (var i = 0; i < flagElements.length; i++) {
|
||||
flagsArray[i] = flagElements[i].title;
|
||||
}
|
||||
|
||||
return flagsArray;
|
||||
},
|
||||
|
||||
// TODO: We should pass a bool to this?
|
||||
gray: state => document.getElementById("append_flag_button").disabled = state === "on" ? true : false
|
||||
};
|
||||
|
||||
/** Prompt to set region if regionVariable is empty */
|
||||
regions = nsetup.load(regionVariable); // TODO: move this to other init stuff
|
||||
if (!regions) {
|
||||
regions = [];
|
||||
setTimeout(function () {
|
||||
window.confirm("Bant Flags: No Flags detected");
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
/** parse the posts already on the page before thread updater kicks in */
|
||||
function parse4chanPosts() {
|
||||
let posts = sliceCall(elementsInClass('postContainer'));
|
||||
|
||||
for (var i = 0; i < posts.length; i++) {
|
||||
let postNumber = posts[i].id.replace("pc", "");
|
||||
postNrs.push(postNumber);
|
||||
}
|
||||
debug(postNrs);
|
||||
}
|
||||
|
||||
function parseFoolFuukaPosts() {
|
||||
let nums = x => x.filter(x => x.id !== '').map(x => x.id);
|
||||
let getPostNumbers = x => nums(sliceCall(elementsInClass(x)));
|
||||
|
||||
postNrs = getPostNumbers('thread').concat(getPostNumbers('post'));
|
||||
debug(postNrs);
|
||||
}
|
||||
|
||||
function onFlagsLoad(response) {
|
||||
|
||||
// because we only care about the end result, not how we got there.
|
||||
let hopHTML = (post_nr, first, second) =>
|
||||
firstChildInClass(firstChildInClass(document.getElementById(post_nr), first), second);
|
||||
|
||||
let MakeFlag = (flag) => createAndAssign('a', {
|
||||
innerHTML: "<img src=\"" + back_end + flag_dir + flag + ".png\" title=\"" + flag + "\">",
|
||||
className: "bantFlag",
|
||||
target: "_blank"
|
||||
});
|
||||
|
||||
debug("JSON: " + response.responseText);
|
||||
var jsonData = JSON.parse(response.responseText);
|
||||
|
||||
jsonData.forEach(function (post) {
|
||||
debug(post);
|
||||
let flagContainer = is_archive
|
||||
? hopHTML(post.post_nr, "post_data", "post_type")
|
||||
: hopHTML("pc" + post.post_nr, "postInfo", "nameBlock");
|
||||
let currentFlag = firstChildInClass(flagContainer, 'flag');
|
||||
let postedRegions = post.region.split(regionDivider);
|
||||
|
||||
// If we have a bantflag and the original post has a flag
|
||||
if (postedRegions.length > 0 && currentFlag !== undefined) {
|
||||
console.log("[BantFlags] Resolving flags for >>" + post.post_nr);
|
||||
|
||||
for (var i = 0; i < postedRegions.length; i++) {
|
||||
let flag = postedRegions[i];
|
||||
|
||||
let newFlag = MakeFlag(flag);
|
||||
if (is_archive) {
|
||||
newFlag.style = "padding: 0px 0px 0px " + (3 + 4 * (i > 0)) + "px; vertical-align:;display: inline-block; width: 16px; height: 11px; position: relative;";
|
||||
}
|
||||
|
||||
flagContainer.append(newFlag);
|
||||
|
||||
console.log("\t -> " + flag);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This can be postNrs.pop()?
|
||||
// postNrs are resolved and should be removed from this variable
|
||||
var index = postNrs.indexOf(post.post_nr);
|
||||
if (index > -1) {
|
||||
postNrs.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: do I need this?
|
||||
// Removing posts older than the time limit (they likely won't resolve)
|
||||
var timestampMinusPostRemoveCounter = Math.round(+new Date() / 1000) - postRemoveCounter; //should i remove this?
|
||||
|
||||
postNrs.forEach(function (post_nr) {
|
||||
let dateTime = is_archive
|
||||
|
||||
// lol didn't expect to get to use this again
|
||||
? hopHTML(post_nr, 'post_data', 'time_wrap')
|
||||
: hopHTML("pc" + post_nr, 'postInfo', 'dateTime');
|
||||
|
||||
if (dateTime.getAttribute("data-utc") < timestampMinusPostRemoveCounter) {
|
||||
var index = postNrs.indexOf(post_nr);
|
||||
if (index > -1) {
|
||||
postNrs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resolveRefFlags() {
|
||||
MakeRequest(
|
||||
"POST",
|
||||
back_end + api_get,
|
||||
"post_nrs=" + encodeURIComponent(postNrs) + "&board=" + encodeURIComponent(boardID) + "&version=" + encodeURIComponent(version),
|
||||
function (resp) {
|
||||
if (resp.status !== 200) {
|
||||
retry(resolveRefFlags, resp);
|
||||
return;
|
||||
}
|
||||
onFlagsLoad(resp);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Flags need to be parsed and aligned differently between boards.
|
||||
if (is_archive) {
|
||||
debug("FoolFuuka.");
|
||||
parseFoolFuukaPosts();
|
||||
|
||||
addGlobalStyle('.bantFlag{top: -2px !important;left: -1px !important}');
|
||||
}
|
||||
else {
|
||||
debug("4chan.");
|
||||
parse4chanPosts();
|
||||
|
||||
addGlobalStyle('.flag{top: 0px !important;left: -1px !important}');
|
||||
addGlobalStyle(".bantFlag {padding: 0px 0px 0px 5px; vertical-align:;display: inline-block; width: 16px; height: 11px; position: relative;}");
|
||||
}
|
||||
|
||||
resolveRefFlags(); // Get flags from db.
|
||||
|
||||
if (!is_archive) {
|
||||
let GetEvDetail = e => e.detail || e.wrappedJSObject.detail;
|
||||
|
||||
let method = "POST",
|
||||
url = back_end + api_post,
|
||||
func = function (resp) {
|
||||
debug(resp.responseText);
|
||||
};
|
||||
|
||||
// TODO: most of this can be rewritten. Do we care to support GM 1.x when we're using ES6?
|
||||
/** send flag to system on 4chan x (v2, loadletter, v3 untested) post
|
||||
* handy comment to save by ccd0
|
||||
* console.log(e.detail.boardID); // board name (string)
|
||||
* console.log(e.detail.threadID); // thread number (integer in ccd0, string in loadletter)
|
||||
* console.log(e.detail.postID); // post number (integer in ccd0, string in loadletter) */
|
||||
document.addEventListener('QRPostSuccessful', function (e) {
|
||||
|
||||
//setTimeout to support greasemonkey 1.x
|
||||
setTimeout(function () {
|
||||
var data = "post_nr=" + encodeURIComponent(e.detail.postID) + "&board=" + encodeURIComponent(e.detail.boardID) + "®ions=" + encodeURIComponent(regions.slice().join(regionDivider));
|
||||
MakeRequest(method, url, data, func);
|
||||
}, 0);
|
||||
}, false);
|
||||
|
||||
/** send flag to system on 4chan inline post */
|
||||
document.addEventListener('4chanQRPostSuccess', function (e) {
|
||||
var evDetail = GetEvDetail(e);
|
||||
|
||||
//setTimeout to support greasemonkey 1.x
|
||||
setTimeout(function () {
|
||||
var data = "post_nr=" + encodeURIComponent(evDetail.postId) + "&board=" + encodeURIComponent(boardID) + "®ions=" + encodeURIComponent(regions.slice().join(regionDivider));
|
||||
MakeRequest(method, url, data, func);
|
||||
}, 0);
|
||||
}, false);
|
||||
|
||||
/** Listen to post updates from the thread updater for 4chan x v2 (loadletter) and v3 (ccd0 + ?) */
|
||||
document.addEventListener('ThreadUpdate', function (e) {
|
||||
var evDetail = GetEvDetail(e);
|
||||
var evDetailClone = typeof cloneInto === 'function' ? cloneInto(evDetail, unsafeWindow) : evDetail;
|
||||
|
||||
//ignore if 404 event
|
||||
if (evDetail[404] === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
|
||||
//add to temp posts and the DOM element to allPostsOnPage
|
||||
evDetailClone.newPosts.forEach(function (post_board_nr) {
|
||||
var post_nr = post_board_nr.split('.')[1];
|
||||
postNrs.push(post_nr);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
//setTimeout to support greasemonkey 1.x
|
||||
setTimeout(resolveRefFlags, 0);
|
||||
}, false);
|
||||
|
||||
/** Listen to post updates from the thread updater for inline extension */
|
||||
document.addEventListener('4chanThreadUpdated', function (e) {
|
||||
var evDetail = GetEvDetail(e);
|
||||
let threadID = window.location.pathname.split('/')[3];
|
||||
let postsContainer = sliceCall(document.getElementById('t' + threadID).childNodes);
|
||||
let lastPosts = postsContainer.slice(Math.max(postsContainer.length - evDetail.count, 1)); //get the last n elements (where n is evDetail.count)
|
||||
|
||||
//add to temp posts and the DOM element to allPostsOnPage
|
||||
lastPosts.forEach(function (post_container) {
|
||||
var post_nr = post_container.id.replace("pc", "");
|
||||
postNrs.push(post_nr);
|
||||
});
|
||||
|
||||
//setTimeout to support greasemonkey 1.x
|
||||
setTimeout(resolveRefFlags, 0);
|
||||
}, false);
|
||||
/** setup init and start first calls */
|
||||
nsetup.init();
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
CREATE DATABASE IF NOT EXISTS bantflags;
|
||||
|
||||
USE bantflags;
|
||||
|
||||
CREATE USER IF NOT EXISTS flags@localhost IDENTIFIED BY 'default';
|
||||
GRANT ALL PRIVILEGES ON bantflags.* TO flags@localhost;
|
||||
FLUSH PRIVILEGES;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `flags` (
|
||||
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||
`flag` VARCHAR(100) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `flag` (`flag`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=0
|
||||
;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `posts` (
|
||||
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||
`post_nr` INT(10) NOT NULL DEFAULT '0',
|
||||
`board` VARCHAR(5) NOT NULL DEFAULT 'bant',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `post_nr` (`post_nr`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=0
|
||||
;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `postflags` (
|
||||
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||
`post_nr` INT(10) NOT NULL DEFAULT '0',
|
||||
`flag` INT(10) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `flag` (`flag`),
|
||||
INDEX `post_nr` (`post_nr`),
|
||||
CONSTRAINT `flag` FOREIGN KEY (`flag`) REFERENCES `flags` (`id`),
|
||||
CONSTRAINT `post_nr` FOREIGN KEY (`post_nr`) REFERENCES `posts` (`id`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=0
|
||||
;
|
||||
|
||||
DROP PROCEDURE IF EXISTS insert_post;
|
||||
DELIMITER $$
|
||||
CREATE DEFINER=`flags`@`localhost` PROCEDURE `insert_post`(
|
||||
IN `@post_nr` INT,
|
||||
IN `@board` VARCHAR(5)
|
||||
)
|
||||
LANGUAGE SQL
|
||||
NOT DETERMINISTIC
|
||||
CONTAINS SQL
|
||||
SQL SECURITY DEFINER
|
||||
COMMENT ''
|
||||
BEGIN
|
||||
INSERT IGNORE INTO `posts` (`post_nr`, `board`) VALUES (`@post_nr`, `@board`);
|
||||
END
|
||||
$$
|
||||
DELIMITER ;
|
||||
|
||||
DROP PROCEDURE IF EXISTS insert_post_flags;
|
||||
DELIMITER $$
|
||||
CREATE DEFINER=`flags`@`localhost` PROCEDURE `insert_post_flags`(
|
||||
IN `@post_nr` INT,
|
||||
IN `@flag` VARCHAR(100)
|
||||
)
|
||||
LANGUAGE SQL
|
||||
NOT DETERMINISTIC
|
||||
CONTAINS SQL
|
||||
SQL SECURITY DEFINER
|
||||
COMMENT ''
|
||||
BEGIN
|
||||
insert into postflags (post_nr, flag) VALUES (
|
||||
(select id from posts where post_nr = `@post_nr`),
|
||||
(select id from flags where flag = `@flag`)
|
||||
);
|
||||
END
|
||||
$$
|
||||
DELIMITER ;
|
Loading…
Reference in new issue