"clsql -> cl-dbi. Added doc comments."

dotnetflags
not manx 4 years ago
parent eaeff37566
commit 2310912b24
Signed by: C-xC-c
GPG Key ID: F52ED472284EF2F4

@ -30,7 +30,7 @@
// see the LICENSE file or <https://www.gnu.org/licenses/> // see the LICENSE file or <https://www.gnu.org/licenses/>
// Change this if you want verbose debuging information in the console. // Change this if you want verbose debuging information in the console.
const debugMode = false; const debugMode = true;
const isGM4 = typeof GM_setValue === 'undefined'; const isGM4 = typeof GM_setValue === 'undefined';
const setValue = isGM4 ? GM.setValue : GM_setValue; const setValue = isGM4 ? GM.setValue : GM_setValue;
@ -40,12 +40,12 @@ const xmlHttpRequest = isGM4 ? GM.xmlHttpRequest : GM_xmlhttpRequest;
// //
// DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR FLAGS SHOULD BE CONFIGURED USING THE FLAG SELECT // DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR FLAGS SHOULD BE CONFIGURED USING THE FLAG SELECT
// //
const version = 2; // Breaking changes. const version = encodeURIComponent(2); // Breaking changes.
const back_end = 'https://flags.plum.moe/'; const back_end = 'https://flags.plum.moe/';
const api_flags = 'api/flags'; const api_flags = back_end + 'api/flags';
const flag_dir = 'flags/'; const flag_dir = back_end + 'flags/';
const api_get = 'api/get'; const api_get = back_end + 'api/get';
const api_post = 'api/post'; const api_post = back_end + 'api/post';
const namespace = 'BintFlegs'; const namespace = 'BintFlegs';
// If you increase this the server will ignore your post. // If you increase this the server will ignore your post.
@ -59,6 +59,11 @@ let flagsLoaded = false;
// DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR FLAGS SHOULD BE CONFIGURED USING THE FLAG SELECT // DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR FLAGS SHOULD BE CONFIGURED USING THE FLAG SELECT
// //
const debug = text => {
if (debugMode)
console.log('[BantFlags] ' + text);
}
// Test unqiue CSS paths to figure out what board software we're using. // Test unqiue CSS paths to figure out what board software we're using.
const software = { const software = {
yotsuba: window.location.host === 'boards.4chan.org', yotsuba: window.location.host === 'boards.4chan.org',
@ -66,29 +71,14 @@ const software = {
foolfuuka: document.querySelector('div[id="main"] article header .post_data') !== null foolfuuka: document.querySelector('div[id="main"] article header .post_data') !== null
}; };
/** Wrapper around Object.assign and document.createElement
* @param {string} element - The HTML Element to create.
* @param {object} source - The properties to assign to the element.
* @returns {object} The HTML tag created */
const createAndAssign = (element, source) => Object.assign(document.createElement(element), source); const createAndAssign = (element, source) => Object.assign(document.createElement(element), source);
const toggleFlagButton = state => document.getElementById('append_flag_button').disabled = state === 'off' ? true : false; const toggleFlagButton = state => document.getElementById('append_flag_button').disabled = state === 'off' ? true : false;
/** Add a stylesheet to the head of the document. const flagSource = flag => flag_dir + flag + ".png";
* @param {string} css - The CSS rules for the stylesheet.
* @returns {object} The style element appended to the head */ /** Add styles to the <head> */
const addGlobalStyle = css => document.head.appendChild(createAndAssign('style', { const addGlobalStyle = css => document.head.appendChild(createAndAssign('style', { innerHTML: css }));
type: 'text/css',
innerHTML: css
}));
/** Write extra information to the console if debugMode is set to true.
* @param {string} text - The text to write to the console. */
function debug(text) {
if (debugMode) {
console.log('[BantFlags] ' + text);
}
}
/** Wrapper around GM_xmlhttpRequest. /** Wrapper around GM_xmlhttpRequest.
* @param {string} method - The HTTP method (GET, POST). * @param {string} method - The HTTP method (GET, POST).
@ -108,7 +98,7 @@ const makeRequest = ((method, url, data, func) => {
/** Itterate over selected flags are store them across browser sessions.*/ /** Itterate over selected flags are store them across browser sessions.*/
function saveFlags() { function saveFlags() {
regions = []; regions = [];
let selectedFlags = document.getElementsByClassName("bantflags_flag"); const selectedFlags = document.querySelectorAll("bantflags_flag");
for (var i = 0; i < selectedFlags.length; i++) { for (var i = 0; i < selectedFlags.length; i++) {
regions[i] = selectedFlags[i].title; regions[i] = selectedFlags[i].title;
@ -118,7 +108,7 @@ function saveFlags() {
} }
/** Add a flag to our selection. /** Add a flag to our selection.
* @param {string} flag - The flag to add to our selection. If no value is passed it takes the current value from the flagSelect. */ * @param {string} flag - The flag to add to our selection. Either passed from saved flags or the current value of flagSelect */
function setFlag(flag) { function setFlag(flag) {
let UID = Math.random().toString(36).substring(7); let UID = Math.random().toString(36).substring(7);
let flagName = flag ? flag : document.querySelector('#flagSelect input').value; let flagName = flag ? flag : document.querySelector('#flagSelect input').value;
@ -126,27 +116,24 @@ function setFlag(flag) {
flagContainer.appendChild(createAndAssign('img', { flagContainer.appendChild(createAndAssign('img', {
title: flagName, title: flagName,
src: back_end + flag_dir + flagName + '.png', src: flagSource(flagName),
id: UID, id: UID,
className: 'bantflags_flag' className: 'bantflags_flag'
})); }));
if (flagContainer.children.length >= max_flags) { if (flagContainer.children.length >= max_flags)
toggleFlagButton('off'); toggleFlagButton('off');
}
document.getElementById(UID).addEventListener("click", (e) => { document.getElementById(UID).addEventListener("click", e => {
flagContainer.removeChild(e.target); flagContainer.removeChild(e.target);
toggleFlagButton('on'); toggleFlagButton('on');
saveFlags(); saveFlags();
}); });
if (!flag) { // When we add a flag to our selection, save it for when we reload the page. if (!flag) // We've added a new flag to our selection
saveFlags(); saveFlags();
}
} }
/** Create flag button and initialise our selected flags */
function init() { function init() {
let flagsForm = createAndAssign('div', { let flagsForm = createAndAssign('div', {
className: 'flagsForm', className: 'flagsForm',
@ -155,15 +142,13 @@ function init() {
// Where do we append the flagsForm to? // Where do we append the flagsForm to?
if (software.yotsuba) { document.getElementById('delform').appendChild(flagsForm); } if (software.yotsuba) { document.getElementById('delform').appendChild(flagsForm); }
if (software.nodegucaDoushio) { document.querySelector('section').append(flagsForm); } // As posts are added the flagForm moves up the page. Could we append this after .section? else if (software.nodegucaDoushio) { document.querySelector('section').append(flagsForm); } // As posts are added the flagForm moves up the page. Could we append this after .section?
for (var i in regions) { for (let i = 0; i < regions.length; i++) {
setFlag(regions[i]); setFlag(regions[i]);
} }
document.getElementById('append_flag_button').addEventListener('click', document.getElementById('append_flag_button').addEventListener('click', () => flagsLoaded ? setFlag() : alert('Load flags before adding them.'));
() => flagsLoaded ? setFlag() : alert('Load flags before adding them.'));
document.getElementById('flagLoad').addEventListener('click', makeFlagSelect, { once: true }); document.getElementById('flagLoad').addEventListener('click', makeFlagSelect, { once: true });
} }
@ -171,14 +156,16 @@ function init() {
function makeFlagSelect() { function makeFlagSelect() {
makeRequest( makeRequest(
"GET", "GET",
back_end + api_flags, api_flags,
"version=" + encodeURIComponent(version), "", // We can't send data, it's a GET request.
function (resp) { function (resp) {
debug('Loading flags.'); debug('Loading flags.');
if (resp.status !== 200) { if (resp.status !== 200) {
return; console.log('Couldn\'t get flag list from server')
} return;
}
let flagSelect = document.getElementById('flagSelect'); let flagSelect = document.getElementById('flagSelect');
let flagList = flagSelect.querySelector('ul'); let flagList = flagSelect.querySelector('ul');
let flagInput = flagSelect.querySelector('input'); let flagInput = flagSelect.querySelector('input');
@ -186,16 +173,18 @@ function makeFlagSelect() {
for (var i = 0; i < flags.length; i++) { for (var i = 0; i < flags.length; i++) {
let flag = flags[i]; let flag = flags[i];
flagList.appendChild(createAndAssign('li', { flagList.appendChild(createAndAssign('li',{
innerHTML: '<img src="' + back_end + flag_dir + flag + '.png" title="' + flag + '"> <span>' + flag + '</span>' innerHTML: `<img src="${flagSource(flag)}" title="${flag}"><span>${flag}</span>`
})); }));
} }
flagSelect.addEventListener('click', (e) => { flagSelect.addEventListener('click', function (e) {
listItem = e.target.nodeName === 'LI' ? e.target : e.target.parentNode; // So we can click the flag image and still select the flag. // So it works if we click the flag image
if (listItem.nodeName === 'LI') { const node = e.target.nodeName === 'LI' ? e.target : e.target.parentNode;
flagInput.value = listItem.querySelector('span').innerHTML; if (node.nodeName === 'LI') {
} flagInput.value = node.querySelector('span').innerHTML;
}
flagList.classList.toggle('hide'); flagList.classList.toggle('hide');
}); });
@ -206,80 +195,67 @@ function makeFlagSelect() {
}); });
} }
/** add all of thhe post numbers on the page to postNrs. /** add all of the post numbers on the page to postNrs. */
* @param {string} selector - The CSS selector who's id is the post number. */
function getPosts(selector) { function getPosts(selector) {
let posts = document.querySelectorAll(selector); const posts = document.querySelectorAll(selector);
for (var i = 0; i < posts.length; i++) {
let postNumber = software.yotsuba
? posts[i].id.replace('pc', '') // Fuck you 4chan.
: posts[i].id;
postNrs.push(postNumber);
}
debug(postNrs);
}
/** Take the response from resolveRefFlags and append flags to their respective post numbers.
* @param {XMLHttpRequest} response - The response data from resolveRefFlags. */
function loadFlags(response) {
debug('JSON: ' + response.responseText);
var jsonData = JSON.parse(response.responseText);
Object.keys(jsonData).forEach(function (post) {
// Get the post header with a CSS selector. Different for each board software.
var flagContainer;
if (software.nodegucaDoushio) { flagContainer = document.querySelector('[id="' + post + '"] header'); }
if (software.yotsuba) { flagContainer = document.querySelector('[id="pc' + post + '"] .postInfo .nameBlock'); }
if (software.foolfuuka) { flagContainer = document.querySelector('[id="' + post + '"] .post_data .post_type'); }
let flags = jsonData[post];
if (flags.length > 0) {
console.log('[BantFlags] Resolving flags for >>' + post);
for (var i = 0; i < flags.length; i++) {
let flag = flags[i];
let newFlag = createAndAssign('a', {
innerHTML: '<img src="' + back_end + flag_dir + flag + '.png" title="' + flag + '">',
className: 'bantFlag',
target: '_blank'
});
if (software.foolfuuka) {
newFlag.style = 'padding: 0px 0px 0px ' + (3 + 2 * (i > 0)) + 'px; vertical-align:;display: inline-block; width: 16px; height: 11px; position: relative;';
}
if (software.nodegucaDoushio) { for (let i = 0; i < posts.length; i++) {
newFlag.title = flag; const postNumber = software.yotsuba
} ? posts[i].id.substr(2) // Fuck you 4chan.
: posts[i].id;
flagContainer.append(newFlag);
console.log('\t -> ' + flag);
}
}
});
postNrs = []; postNrs.push(postNumber);
}
debug(postNrs);
} }
/** Get flags from the database using values in postNrs and pass the response on to onFlagsLoad */ /** Get flags from the database using values in postNrs and pass the response on to onFlagsLoad */
function resolveFlags() { function resolveFlags() {
debug('Board is: ' + board_id);
makeRequest( makeRequest(
'POST', 'POST',
back_end + api_get, api_get,
'post_nrs=' + encodeURIComponent(postNrs) + '&board=' + encodeURIComponent(board_id) + '&version=' + encodeURIComponent(version), 'post_nrs=' + encodeURIComponent(postNrs) + '&board=' + encodeURIComponent(board_id) + '&version=' + version,
function (resp) { function (resp) {
if (resp.status !== 200) { if (resp.status !== 200) {
console.log('[bantflags] Couldn\'t load flags. Refresh the page.'); console.log('[bantflags] Couldn\'t load flags. Refresh the page.');
return; return;
} }
loadFlags(resp);
} const jsonData = JSON.parse(resp.responseText);
); debug(`JSON: ${resp.responseText}`);
Object.keys(jsonData).forEach(post => {
let flags = jsonData[post];
if (flags.length <= 0)
return;
debug(`Resolving flags for >>${post}`);
let flagContainer;
if (software.yotsuba) { flagContainer = document.querySelector(`[id="pc${post}"] .postInfo .nameBlock`); }
else if (software.foolfuuka) { flagContainer = document.querySelector(`[id="${post}"] .post_data .post_type`); }
else if (software.nodegucaDoushio) { flagContainer = document.querySelector(`[id="${post}"] header`); }
for (let i = 0; i < flags.length; i++) {
const flag = flags[i];
const newFlag = createAndAssign('a', {
innerHTML: `<img src="${flagSource(flag)}" title="${flag}">`,
className: 'bantFlag',
target: '_blank',
title: flag
});
flagContainer.append(newFlag);
debug(`\t -> ${flag}`);
}
});
postNrs = [];
});
} }
function main() { function main() {
@ -289,19 +265,16 @@ function main() {
} }
// See Docs/styles.css // See Docs/styles.css
addGlobalStyle('.flagsForm{float: right; clear: right; margin: 20px 10px;} #flagSelect{display:none;} .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;}} #flagSelect ul {list-style-type: none;padding: 0;margin-bottom: 0;cursor: pointer;bottom: 100%;height: 200px;overflow: auto;position: absolute;width:200px;background-color:#fff}#flagSelect ul li {display: block;}#flagSelect ul li:hover {background-color: #ddd;}#flagSelect {position: absolute;}#flagSelect input {width: 200px;} #flagSelect .hide {display: none;}#flagSelect img {margin-left: 2px;}'); addGlobalStyle('.bantFlag{padding: 0px 0px 0px 5px; display: inline-block; width: 16px; height: 11px; position: relative;} .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;}}.flagsForm{float: right; clear: right; margin: 20px 10px;} #flagSelect{display:none;} #flagSelect ul{list-style-type: none;padding: 0;margin-bottom: 0;cursor: pointer;bottom: 100%;height: 200px;overflow: auto;position: absolute;width:200px;background-color:#fff} #flagSelect ul li {display: block;} #flagSelect ul li:hover {background-color: #ddd;}#flagSelect {position: absolute;}#flagSelect input {width: 200px;} #flagSelect .hide {display: none;}#flagSelect img {margin-left: 2px;}')
// We get flags using different selectors, and we need to align them differently.
if (software.yotsuba) { if (software.yotsuba) {
debug('4chan');
getPosts('.postContainer'); getPosts('.postContainer');
addGlobalStyle('.bantFlag {padding: 0px 0px 0px 5px; vertical-align:;display: inline-block; width: 16px; height: 11px; position: relative;} .flag{top: 0px;left: -1px}'); addGlobalStyle('.flag{top: 0px;left: -1px}');
init(); init();
} }
if (software.nodegucaDoushio) { if (software.nodegucaDoushio) {
debug('Nineball');
getPosts('section[id], article[id]'); getPosts('section[id], article[id]');
addGlobalStyle('.bantFlag {cursor: default} .bantFlag img {pointer-events: none;}'); addGlobalStyle('.bantFlag {cursor: default} .bantFlag img {pointer-events: none;}');
@ -309,17 +282,18 @@ function main() {
} }
if (software.foolfuuka) { if (software.foolfuuka) {
debug('FoolFuuka');
getPosts('article[id]'); getPosts('article[id]');
addGlobalStyle('.bantFlag{top: -2px !important;left: -1px !important}'); addGlobalStyle('.bantFlag{top: -2px !important;left: -1px !important}');
} }
board_id = window.location.pathname.split('/')[1]; board_id = window.location.pathname.split('/')[1];
resolveFlags(); debug(board_id);
resolveFlags();
} }
if (isGM4) { if (isGM4) { // Fuck you GM4
(async () => { (async () => {
regions = await getValue(namespace); regions = await getValue(namespace);
main(); main();
@ -330,20 +304,20 @@ else {
main(); main();
} }
const postFlags = (post_nr, func = resp => debug(resp.responseText)) => makeRequest(
'POST',
api_post,
`post_nr=${encodeURIComponent(post_nr)}&board=${encodeURIComponent(board_id)}&regions=${encodeURIComponent(regions)}&version=${version}`,
func);
if (software.yotsuba) { if (software.yotsuba) {
const GetEvDetail = e => e.detail || e.wrappedJSObject.detail; const GetEvDetail = e => e.detail || e.wrappedJSObject.detail;
// 4chanX and native extension respectively
document.addEventListener('QRPostSuccessful', e => postFlags(e.detail.postID));
document.addEventListener('4chanQRPostSuccess', e => postFlags(GetEvDetail(e).postId));
const postFlags = post_nr => makeRequest( // I need to look at these.
'POST',
back_end + api_post,
'post_nr=' + encodeURIComponent(post_nr) + '&board=' + encodeURIComponent(board_id) + '&regions=' + encodeURIComponent(regions) + '&version=' + encodeURIComponent(version),
func = resp => debug(resp.responseText));
// Send flags to the backend when we makle a post. Top is 4chanX, bottom is native extension.
document.addEventListener('QRPostSuccessful', e => postFlags(e.detail.postID), false);
document.addEventListener('4chanQRPostSuccess', e => postFlags(GetEvDetail(e).postId), false);
// I need to look at these.
document.addEventListener('ThreadUpdate', function (e) { document.addEventListener('ThreadUpdate', function (e) {
var evDetail = GetEvDetail(e); var evDetail = GetEvDetail(e);
var evDetailClone = typeof cloneInto === 'function' ? cloneInto(evDetail, unsafeWindow) : evDetail; var evDetailClone = typeof cloneInto === 'function' ? cloneInto(evDetail, unsafeWindow) : evDetail;
@ -373,44 +347,49 @@ if (software.yotsuba) {
}); });
resolveFlags(); resolveFlags();
}, false); }, false);
} }
if (software.nodegucaDoushio) { if (software.nodegucaDoushio) {
// This is poking at the mutations made on the page to figure out what happened and thus what actions to take.
// There is full support for nodeguca but I don't have a Doushio board I feel comfortable spamming to ensure it works properly there. There is at least partial support. const postFunc = function() {
new MutationObserver(function (mutations) { postNrs.push(mutation.target.id);
mutations.forEach(function (mutation) { resolveFlags();
if (mutation.addedNodes.length > 0) { // A post was added. }
var firstAddedNode = mutation.addedNodes[0].nodeName;
const badNodes = ['HR', 'SECTION'];
// Enter a thread or change boards. Checks for when a post is added while in the index.
if (mutation.target.nodeName === 'THREADS' && firstAddedNode !== 'HR' && firstAddedNode !== 'SECTION') { new MutationObserver(mutations => {
board_id = window.location.pathname.split('/')[1]; mutations.forEach(mutation => {
setTimeout(getPosts('section[id], article[id]'), 2000); if (mutation.addedNodes.length <= 0)
resolveFlags(); return; // We only care if something post was added
init();
} var firstAddedNode = mutation.addedNodes[0].nodeName;
// You post. // Enter a thread / change boards
if (firstAddedNode === 'HEADER') { if (mutation.target.nodeName === 'THREADS') {
let data = 'post_nr=' + encodeURIComponent(mutation.target.id) + '&board=' + encodeURIComponent(board_id) + '&regions=' + encodeURIComponent(regions) + '&version=' + encodeURIComponent(version); if (badNodes.includes(firstAddedNode))
makeRequest( return; // We are in the index and a post was added, handled properly further down
'POST',
back_end + api_post, board_id = window.location.pathname.split('/')[1];
data, setTimeout(getPosts('section[id], article[id]'), 2000);
function () { resolveFlags();
postNrs.push(mutation.target.id); init();
resolveFlags();
});
}
// Someone else posts. Checks to see if you're hovering over a post.
if (firstAddedNode === 'ARTICLE' && mutation.target.nodeName !== "BODY" && mutation.target.id !== 'hover_overlay') {
postNrs.push(mutation.addedNodes[0].id);
setTimeout(resolveFlags, 1500);
}
} }
});
}).observe(document.body, { childList: true, subtree: true }); // We post
if (firstAddedNode === 'HEADER') {
postFlags(mutation.target.id, postFunc)
}
// Someone else posts
if (firstAddedNode === 'ARTICLE') {
if (mutation.target.nodeName === 'BODY' || mutation.target.id === 'hover_overlay')
return; // User is hovering over a post
postNrs.push(mutation.addedNodes[0].id);
setTimeout(resolveFlags, 1500);
}
});
}).observe(document.body, { childList: true, subtree: true });
} }

@ -7,7 +7,7 @@
:serial t :serial t
:depends-on (:hunchentoot :depends-on (:hunchentoot
:str :str
:clsql :cl-dbi
:jonathan) :jonathan)
:Components :Components
((:file "utils") ((:file "utils")

@ -1,6 +1,6 @@
(defvar config (defvar config
'((boards "bant") '((boards "bant")
(staging-password "not implemented") (staging-password "not implemented")
(db-conn "localhost" "bantflags" "flags" "default") (db-conn "bantflags" "flags" "default")
(poolsize 3) (poolsize 3)
(www-root #p"/path/to/files/"))) (www-root #p"/path/to/files/")))

@ -1,33 +1,52 @@
;; Databases in common lisp are the fucking worst.
;; Don't even bother.
;; Comparing strings with both ;; Comparing strings with both
(defparameter *flags* (make-hash-table :test 'equal)) (defparameter *flags* (make-hash-table :test 'equal))
(defparameter *boards* (make-hash-table :test 'equal)) (defparameter *boards* (make-hash-table :test 'equal))
(defparameter *flags-txt* nil) (defparameter *flags-txt* nil)
(defparameter conn nil) (defparameter conn-str nil)
(defvar get-posts-sql "SELECT posts.post_nr, flags.flag from flags left join postflags on (postflags.flag = flags.id) left join posts on (postflags.post_nr = posts.id) where posts.post_nr in (~{'~a'~^,~}) and posts.board = '~a';") (defvar get-posts-sql "SELECT posts.post_nr, flags.flag from flags left join postflags on (postflags.flag = flags.id) left join posts on (postflags.post_nr = posts.id) where posts.post_nr in (~{'~a'~^,~}) and posts.board = '~a';")
;; (clsql:start-sql-recording)
(defun fuck-you-fukamachi (conn |;_;|)
"What the fuck is going on with what dbi:fetch returns? why is it a
fucking list with the database columns as a symbols with pipes around
them? How in the dicking shit am I supposed to use :|post_nr| 1234 in
any useful or practical way? Why does this fucking Wumpus of a human
being Fukamachi feel the need to duplicate so much data? Don't get me
wrong, clsql is easily worse to work with, but at least it was fucking
smart enough to make the database fields into (values rows columns)"
(mapcar (lambda (x) (list (nth 1 x) (nth 3 x)))
(dbi:fetch-all (dbi:execute (dbi:prepare conn |;_;|)))))
(defmacro dbfun (name &rest body) (defmacro dbfun (name &rest body)
`(defun ,name ,(car body) `(defun ,name ,(car body)
(clsql:with-database (db conn :database-type :mysql :pool t) (dbi:with-connection (conn :mysql
:database-name (car conn-str)
:username (nth 1 conn-str)
:password (nth 2 conn-str))
,@(cdr body)))) ,@(cdr body))))
(defun flag-id (flag) (defun flag-id (flag)
(gethash flag *flags*)) (gethash flag *flags*))
(dbfun ping ()
(dbi:ping conn))
(dbfun insert-post (post_nr board flags) (dbfun insert-post (post_nr board flags)
(clsql:query (format nil "insert ignore into posts (post_nr, board) values (~a, '~a');" post_nr board)) (dbi:do-sql conn
(let ((post-id (caar (clsql:query (format nil "select id from posts where post_nr = ~a and board = '~a';" post_nr board))))) (format nil "insert ignore into posts (post_nr, board) values (~a, '~a');" post_nr board))
(clsql:execute-command (let ((post-id (cadr (dbi:fetch (dbi:execute (dbi:prepare conn "select id from posts where post_nr = 9999 and board = 'bant';"))))))
(with-output-to-string (s) (dbi:do-sql conn
(format s "insert into postflags (post_nr, flag) values") (with-output-to-string (s)
(loop for flag in (butlast flags) (format s "insert into postflags (post_nr, flag) values")
do (format s "(~a,~a)," post-id (flag-id flag))) (loop for flag in (butlast flags)
(format s "(~a,~a);" post-id (flag-id (car (last flags)))) do (format s "(~a,~a)," post-id (flag-id flag)))
:database db)))) (format s "(~a,~a);" post-id (flag-id (car (last flags))))))))
(dbfun get-posts (posts board) (dbfun get-posts (posts board)
(let ((result (clsql:query (format nil get-posts-sql posts board) :database db)) (let ((result (fuck-you-fukamachi conn (format nil get-posts-sql posts board)))
(table (make-hash-table))) (table (make-hash-table)))
(loop for (post_nr . flag) in result do (loop for (post_nr . flag) in result do
(unless (gethash post_nr table) (unless (gethash post_nr table)
@ -36,4 +55,4 @@
(jojo:to-json table))) (jojo:to-json table)))
(dbfun get-flags () (dbfun get-flags ()
(clsql:query "select flags.id, flags.flag from flags" :database db)) (fuck-you-fukamachi conn "select flags.id, flags.flag from flags"))

@ -1,9 +1,11 @@
(defun init () (defun init ()
(set-db-conn) (set-db-conn)
(dotimes (_ (cconf 'poolsize)) (dotimes (_ (cconf 'poolsize))
(clsql:connect conn :database-type :mysql :pool t :if-exists :new)) (dbi:connect-cached :mysql
(when (eq nil clsql:*default-database*) :database-name (car conn-str)
(error "fucked up connecting to database")) :username (nth 1 conn-str)
:password (nth 2 conn-str)))
(ping) ;; test db conn
(set-boards) (set-boards)
(set-flags) (set-flags)
(defvar +serb+ (make-instance 'hunchentoot:easy-acceptor (defvar +serb+ (make-instance 'hunchentoot:easy-acceptor
@ -42,7 +44,7 @@
(handle :post (api-get :uri "/staging/get") (handle :post (api-get :uri "/staging/get")
(post_nrs board version) (post_nrs board version)
(@json *reply*) (@json tbnl:*reply*)
(setf post_nrs (str:split "," post_nrs)) (setf post_nrs (str:split "," post_nrs))
(cond (cond
((and (loop for x in post_nrs always (post-number-p x)) ((and (loop for x in post_nrs always (post-number-p x))

@ -24,7 +24,7 @@
(format nil "~{~a~^~%~}" (mapcan (lambda (x) (cdr x)) flags)) (format nil "~{~a~^~%~}" (mapcan (lambda (x) (cdr x)) flags))
"")))) ""))))
(defun set-db-conn () (defun set-db-conn ()
(setq conn (conf 'db-conn))) (setq conn-str (conf 'db-conn)))
(defun get-version (thing) (defun get-version (thing)
(if (null thing) 0 (if (null thing) 0

Loading…
Cancel
Save