Eerste commit
This commit is contained in:
commit
f84a4f321c
8
dub.sdl
Normal file
8
dub.sdl
Normal file
|
@ -0,0 +1,8 @@
|
|||
name "kerstezel"
|
||||
description "A simple vibe.d server application."
|
||||
authors "vercie"
|
||||
copyright "Copyright © 2020, vercie"
|
||||
license "MIT"
|
||||
dependency "vibe-d" version="~>0.9"
|
||||
stringImportPaths "views" "translations"
|
||||
versions "VibeOutputCompactHTML"
|
17
dub.selections.json
Normal file
17
dub.selections.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"fileVersion": 1,
|
||||
"versions": {
|
||||
"botan": "1.12.18",
|
||||
"botan-math": "1.0.3",
|
||||
"diet-ng": "1.7.4",
|
||||
"eventcore": "0.9.11",
|
||||
"libasync": "0.8.6",
|
||||
"memutils": "1.0.4",
|
||||
"mir-linux-kernel": "1.0.1",
|
||||
"openssl": "1.1.6+1.0.1g",
|
||||
"stdx-allocator": "2.77.5",
|
||||
"taggedalgebraic": "0.11.18",
|
||||
"vibe-core": "1.11.1",
|
||||
"vibe-d": "0.9.2"
|
||||
}
|
||||
}
|
76
source/app.d
Normal file
76
source/app.d
Normal file
|
@ -0,0 +1,76 @@
|
|||
import std.functional;
|
||||
|
||||
import vibe.vibe;
|
||||
|
||||
import kerstezelprotocol;
|
||||
|
||||
|
||||
|
||||
struct VertaalContext {
|
||||
import std.typetuple;
|
||||
// Throw an error when an translation string is missing/mistyped.
|
||||
enum enforceExistingKeys = true;
|
||||
// The list of supported languages (the same family of languages will
|
||||
// automatically be matched to the closest candidate, e.g. en_GB->en_US)
|
||||
alias languages = TypeTuple!("en_GB", "nl_NL");
|
||||
// The base name of the translation files - the full names will be
|
||||
// kerstman.en_US.po and kerstman.de_DE.po. Any number of these mixin
|
||||
// statements can be used.
|
||||
mixin translationModule!"kerstman";
|
||||
}
|
||||
|
||||
@translationContext!VertaalContext
|
||||
class KerstezelService {
|
||||
|
||||
@path("/")
|
||||
void getIndex() {
|
||||
render!"index.dt";
|
||||
}
|
||||
|
||||
@path("/arreslee")
|
||||
void getArreslee(scope WebSocket bus) {
|
||||
runTask((WebSocket bus) {
|
||||
while(bus.connected) {
|
||||
string commando = bus.receiveText;
|
||||
if (commando == "trommel") {
|
||||
kerstmanTaak.send(TrommelCommando());
|
||||
} else {
|
||||
logInfo("Onbekend commando:" ~ commando);
|
||||
}
|
||||
}
|
||||
}, bus);
|
||||
|
||||
while (bus.connected) {
|
||||
int uitzend_aantal = staatGebeurtenis.emitCount;
|
||||
bus.send(staat.serializeToJsonString());
|
||||
// Wordt elke 30 seconden wakker om de verbinding in leven
|
||||
// te houden en/of te detecteren dat deze juist gestopt is.
|
||||
staatGebeurtenis.wait(30.seconds, uitzend_aantal);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
import vibe.http.router;
|
||||
|
||||
auto settings = new HTTPServerSettings;
|
||||
settings.port = 8090;
|
||||
settings.bindAddresses = ["::", "0.0.0.0"];
|
||||
|
||||
auto router = new URLRouter;
|
||||
router.registerWebInterface(new KerstezelService);
|
||||
|
||||
auto fsettings = new HTTPFileServerSettings;
|
||||
fsettings.serverPathPrefix = "/statisch";
|
||||
router.get("*", serveStaticFiles("statisch/", fsettings));
|
||||
|
||||
listenHTTP(settings, router);
|
||||
|
||||
logInfo("Please open http://127.0.0.1:8080/ in your browser.");
|
||||
if (!listenTCP(9305, toDelegate(&kerstezelProtocolLuister), "0.0.0.0")) {
|
||||
logWarn("Kon kerstezelProtocol niet starten");
|
||||
}
|
||||
runApplication();
|
||||
}
|
142
source/kerstezelprotocol.d
Normal file
142
source/kerstezelprotocol.d
Normal file
|
@ -0,0 +1,142 @@
|
|||
import std.array;
|
||||
import std.algorithm;
|
||||
import std.datetime;
|
||||
import std.exception;
|
||||
import std.format;
|
||||
import std.random;
|
||||
import std.range;
|
||||
|
||||
import vibe.vibe;
|
||||
|
||||
immutable size_t BUF_SIZE = 2048;
|
||||
|
||||
struct Staat {
|
||||
bool verbondenMetKerstman = false;
|
||||
long trommelenVerbodenTot = 0;
|
||||
}
|
||||
|
||||
|
||||
shared Staat staat;
|
||||
shared ManualEvent staatGebeurtenis;
|
||||
shared Task kerstmanTaak;
|
||||
|
||||
__gshared TaskMutex mutex;
|
||||
shared static this() {
|
||||
staatGebeurtenis = createSharedManualEvent();
|
||||
mutex = new TaskMutex();
|
||||
}
|
||||
|
||||
struct TrommelCommando {};
|
||||
|
||||
@trusted
|
||||
void kerstezelProtocolLuister(TCPConnection verbinding) nothrow {
|
||||
try {
|
||||
auto lock = scopedMutexLock(mutex);
|
||||
new Sessie(verbinding).voerUit();
|
||||
} catch (Exception e) {
|
||||
logException(e, "Kon mutex niet verkrijgen");
|
||||
}
|
||||
}
|
||||
|
||||
class UnicodeUitzondering : Exception {
|
||||
mixin basicExceptionCtors;
|
||||
}
|
||||
|
||||
class ProtocolUitzondering : Exception {
|
||||
mixin basicExceptionCtors;
|
||||
}
|
||||
|
||||
class Sessie {
|
||||
private TCPConnection l_verbinding;
|
||||
private bool blijfBezig = true;
|
||||
private SysTime trommelenVerbodenTot;
|
||||
private bool trommelenVerboden = false;
|
||||
|
||||
@safe
|
||||
this(TCPConnection verbinding) nothrow {
|
||||
this.l_verbinding = verbinding;
|
||||
}
|
||||
|
||||
@trusted
|
||||
public void voerUit() nothrow {
|
||||
logInfo("Nieuwe sessie probeert te verbinden");
|
||||
try {
|
||||
handGeven();
|
||||
logInfo("Sessie is verbonden");
|
||||
staat.verbondenMetKerstman = true;
|
||||
kerstmanTaak = Task.getThis();
|
||||
staatGebeurtenis.emit();
|
||||
|
||||
while(true) {
|
||||
auto tijdsduur = trommelenVerboden
|
||||
? min(trommelenVerbodenTot - Clock.currTime, seconds(30))
|
||||
: seconds(30);
|
||||
|
||||
bool received = receiveTimeout(tijdsduur,
|
||||
(TrommelCommando com) {
|
||||
logInfo("Trommelen");
|
||||
auto now = Clock.currTime();
|
||||
if (trommelenVerboden) return;
|
||||
l_verbinding.write("trommel\n");
|
||||
l_verbinding.flush();
|
||||
trommelenVerboden = true;
|
||||
trommelenVerbodenTot = now + seconds(30);
|
||||
staat.trommelenVerbodenTot = trommelenVerbodenTot.toUnixTime;
|
||||
staatGebeurtenis.emit();
|
||||
|
||||
ubyte[] read = l_verbinding.readLine(BUF_SIZE, "\n");
|
||||
enforce!ProtocolUitzondering(read.equal("oké"), "Kerstman deed geen erkenning, maar deed %s".format(read));
|
||||
}
|
||||
);
|
||||
if (Clock.currTime() >= trommelenVerbodenTot) {
|
||||
trommelenVerboden = false;
|
||||
staatGebeurtenis.emit();
|
||||
}
|
||||
if (!received) {
|
||||
l_verbinding.write("ping\n");
|
||||
l_verbinding.flush();
|
||||
ubyte[] read = l_verbinding.readLine(BUF_SIZE, "\n");
|
||||
enforce!ProtocolUitzondering(read.equal("pong"), "Kerstman pongde niet terug, maar deed %s".format(read));
|
||||
}
|
||||
}
|
||||
} catch (ProtocolUitzondering u) {
|
||||
logException(u, "Protocoluitzondering");
|
||||
} catch (Exception e) {
|
||||
logException(e, "Andere uitzondering");
|
||||
} finally {
|
||||
logInfo("Sessie verbroken");
|
||||
staat.verbondenMetKerstman = false;
|
||||
staatGebeurtenis.emit();
|
||||
sluit();
|
||||
}
|
||||
}
|
||||
|
||||
@safe
|
||||
private void handGeven () {
|
||||
// Maak een begroeting
|
||||
string begroeting2 = "e gaat 'ie?\n";
|
||||
// Maak het irritant om het met de hand te doen :P
|
||||
int aantal = cast(int) (rndGen.front % 200) + 100;
|
||||
logInfo("Aantal: %d".format(aantal));
|
||||
rndGen.popFront();
|
||||
|
||||
string begroeting = "ho".replicate(aantal) ~ begroeting2;
|
||||
l_verbinding.write(begroeting);
|
||||
l_verbinding.flush();
|
||||
|
||||
// Wacht op een antwoord in de vorm van (aantal * "go" + "ed!")
|
||||
ubyte[] data = l_verbinding.readLine(BUF_SIZE, "\n");
|
||||
string expected = "go".replicate(aantal) ~ "ed";
|
||||
enforce!ProtocolUitzondering(equal(data, expected), "Ongeldige handdruk");
|
||||
}
|
||||
|
||||
@safe
|
||||
private void sluit() nothrow {
|
||||
try {
|
||||
l_verbinding.close();
|
||||
} catch(Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
BIN
statisch/kerstman.jpg
Normal file
BIN
statisch/kerstman.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
BIN
statisch/rand-klein.png
Normal file
BIN
statisch/rand-klein.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
BIN
statisch/rand.png
Normal file
BIN
statisch/rand.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 MiB |
1
statisch/sneeuw2.min.js
vendored
Normal file
1
statisch/sneeuw2.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
snow={count:60,delay:20,flutter:0.2,wobble:0.5,spin:1,wind:1,w1:1,minSpeed:0.3,maxSpeed:4,cv:null,flakes:[],toggle:function(){if(window.snowtimer){snow.stop()}else{snow.start()}},resize:function(){snow.cv.width=innerWidth;snow.cv.height=innerHeight;snow.gt=snow.ct.createLinearGradient(0,0,0,snow.cv.height);snow.gt.addColorStop(0,"#6666ff");snow.gt.addColorStop(1,"#ffffff");snow.ct.fillStyle=snow.gt},start:function(){snow.cv=document.createElement("canvas");snow.cv.width=snow.cv.height=10;snow.cv.id="backgroundSnowCanvas";document.body.appendChild(snow.cv);snow.createFlake();snow.ct=snow.cv.getContext("2d"),snow.cv.style.position="fixed";snow.cv.style.top=0;snow.cv.style.left=0;snow.cv.style.zIndex=-1;snow.resize();var a=snow.count;snow.flakes=[];do{snow.flakes.push(new snow.flake())}while(--a);snow.ct.clearRect(0,0,snow.cv.width,snow.cv.height);window.snowtimer=window.setInterval(snow.draw,snow.delay);window.addEventListener("resize",snow.resize)},stop:function(){window.clearInterval(window.snowtimer);var a=document.getElementById("backgroundSnowCanvas");a.parentNode.removeChild(a);window.snowtimer=snow=null},draw:function(){var a=snow.ct,b=snow.flakes,d=snow.count;a.clearRect(0,0,snow.cv.width,snow.cv.height);do{if(b[--d].draw(a)&&++fdone){}}while(d);snow.wind+=Math.cos(snow.w1++/180)},flake:function(){this.draw=function(c){var b=this.x+snow.wind,e=this.y,a=b+this.sz/2,d=e+this.sz/2;c.translate(a,d);c.rotate(this.a);c.translate(-a,-d);c.drawImage(snow.flakeImages[this.flake],b,e,this.sz,this.sz);if(this.flakebits>=0){c.drawImage(snow.flakeBits[this.flakebits],b,e,this.sz,this.sz)}c.setTransform(1,0,0,1,0,0);this.animate()};this.animate=function(){this.y+=this.speed;this.x+=this.flutter*Math.cos(snow.flutter*snow.flutter*this.y);this.a=(this.spin*this.y)+(this.wobble*Math.sin(this.y/this.sz));if(this.y>innerHeight){this.init(1)}};this.init=function(a){this.speed=snow.minSpeed+(Math.random()*(snow.maxSpeed-snow.minSpeed));this.sz=~~(Math.random()*40)+20;this.flutter=~~(Math.random()*snow.flutter*(60-this.sz));this.wobble=Math.random()*snow.wobble;this.spin=snow.spin*0.1*(Math.random()-0.5);this.a=0;this.x=(Math.random()*(innerWidth+this.sz))-this.sz;this.y=a?-this.sz:Math.random()*innerHeight;this.flake=~~(Math.random()*snow.flakeImages.length);this.flakebits=~~(Math.random()*(snow.flakeBits.length+1))-1};this.init()},createFlake:function(){var A,z,B,s,r,j=document.createElement("canvas"),m,d;j.width=j.height=40;snow.flakeImages=[];snow.flakeBits=[];for(A=0;A<6;++A){snow.flakeImages[A]=s=j.cloneNode();B=s.getContext("2d");B.fillStyle="#fff";B.translate(20,20);B.beginPath();r=1+(A/2);B.rect(-r,-20,r*2,40);B.rotate(Math.PI/3);B.rect(-r,-20,r*2,40);B.rotate(Math.PI/3);B.rect(-r,-20,r*2,40);B.closePath();B.fill()}function x(f){f.arc(0,-16,4,0,7,0)}function b(I,F,f,H,G,g){I.moveTo(0,-f);I.lineTo(F,-H);I.lineTo(F,-G);I.lineTo(0,-H-g);I.lineTo(-F,-G);I.lineTo(-F,-H);I.lineTo(0,-f)}function i(f){b(f,5,6,8,10,0)}function v(f){b(f,5,10,12,14,0)}function u(f){b(f,4,6,8,12,2)}function k(f){b(f,9,6,8,11,0)}function y(f){b(f,4,16,16,18,2)}function E(f){b(f,5,12,12,14,2)}function e(f){x(f);u(f)}function t(f){v(f);u(f)}function l(f){i(f);v(f)}function o(f){x(f);i(f)}function a(f){y(f);E(f)}function D(f){y(f);k(f)}function n(f){y(f);u(f)}function C(f){y(f);i(f)}function q(f){E(f);i(f)}function p(f){y(f);v(f)}function h(f){x(f);k(f)}d=[h,q,p,C,n,D,y,E,a,k,x,i,v,u,e,t,l,o];for(A=0;A<d.length;++A){snow.flakeBits[A]=s=j.cloneNode();m=d[A];B=s.getContext("2d");B.translate(20,20);B.fillStyle="#fff";for(z=0;z<6;++z){B.beginPath();m(B);B.closePath();B.fill();B.rotate(Math.PI/3)}}}};
|
45
statisch/stijl.css
Normal file
45
statisch/stijl.css
Normal file
|
@ -0,0 +1,45 @@
|
|||
html {
|
||||
background-color: red;
|
||||
color: green;
|
||||
}
|
||||
body {
|
||||
font-family: "sans-serif";
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 2em auto;
|
||||
max-width: 600px;
|
||||
padding: 1.5em;
|
||||
border-image: url("rand-klein.png") 70 65 / 80px / 30px round;
|
||||
border-style: solid;
|
||||
background-color: cyan;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 10px outset green;
|
||||
background-color: green;
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: darkgreen;
|
||||
border-style: groove;
|
||||
color: red;
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
button:active, button:active:not(:disabled) {
|
||||
border-style: inset;
|
||||
}
|
69
statisch/webbus.js
Normal file
69
statisch/webbus.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
var knop;
|
||||
var bus;
|
||||
|
||||
function werkKnopBij(tekst, aan) {
|
||||
knop.innerText = tekst;
|
||||
knop.disabled = !aan;
|
||||
}
|
||||
|
||||
function trommel() {
|
||||
bus.send("trommel");
|
||||
}
|
||||
|
||||
function aftellen(tijd) {
|
||||
if (bus.readyState !== bus.OPEN) return;
|
||||
werkKnopBij(v9n["knop.afkoelen"].replace("{seconden}", Math.ceil((tijd - Date.now()) / 1000)), false);
|
||||
if (tijd - Date.now() > 0) {
|
||||
window.setTimeout(function() { aftellen(tijd) }, 1000);
|
||||
} else {
|
||||
werkKnopBij(v9n["knop.activeer"], true);
|
||||
}
|
||||
}
|
||||
|
||||
function verbind() {
|
||||
werkKnopBij(v9n["knop.initialiseren"], false);
|
||||
var bestemming =( window.location.protocol == "http:"
|
||||
? "ws://" : "wss://") + window.location.hostname + ":" + window.location.port + "/arreslee";
|
||||
bus = new WebSocket(bestemming);
|
||||
bus.onopen = function() {
|
||||
werkKnopBij(v9n["knop.initialiseren2"], false);
|
||||
knop.removeEventListener("click", verbind);
|
||||
knop.addEventListener("click", trommel);
|
||||
}
|
||||
bus.onmessage = function(bericht) {
|
||||
var gegevens = JSON.parse(bericht.data);
|
||||
if (gegevens.verbondenMetKerstman) {
|
||||
var doelTijd = gegevens.trommelenVerbodenTot * 1000;
|
||||
var tijdVerschil = Date.now() - doelTijd;
|
||||
if (tijdVerschil > 0) {
|
||||
werkKnopBij(v9n["knop.activeer"], true);
|
||||
} else {
|
||||
aftellen(doelTijd);
|
||||
}
|
||||
} else {
|
||||
werkKnopBij(v9n["knop.initialiseren2"], false);
|
||||
}
|
||||
}
|
||||
bus.onclose = function() {
|
||||
werkKnopBij("Opnieuw verbinden", true);
|
||||
knop.removeEventListener("click", trommel);
|
||||
knop.addEventListener("click", verbind);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
knop = document.getElementById("knop");
|
||||
verbind();
|
||||
|
||||
try {
|
||||
snow.count = 100; // number of flakes
|
||||
snow.delay = 20; // timer interval
|
||||
snow.minSpeed = 0.2; // minimum movement/time slice
|
||||
snow.maxSpeed = 2; // maximum movement/time slice
|
||||
snow.wobble = 2;
|
||||
snow.flutter = 0.1;
|
||||
snow.start();
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
29
translations/kerstman.en_GB.po
Normal file
29
translations/kerstman.en_GB.po
Normal file
|
@ -0,0 +1,29 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\\n"
|
||||
|
||||
msgid "pagina.titel"
|
||||
msgstr "Father Christmas of IAPC"
|
||||
|
||||
msgid "pagina.kop1"
|
||||
msgstr "Our Beloved Father Christmas"
|
||||
|
||||
msgid "pagina.tekst"
|
||||
msgstr ""
|
||||
"At IAPC we have a wonderful miniature Father Christmas. "
|
||||
"This Father Christmas is not your avarage Father Christmas. This Father Christmas is able to drum! He has brought the volunteers at IAPC a lot of <abbr title=\"pain\">joy</abbr> "
|
||||
"over the last serveral years, drumming his way around. "
|
||||
"But this year, due to covid-19, the amount of people around around our store has been historically low, which is why we "
|
||||
"are looking for you! You can save Christmas at IAPC! All you have to do is press the big green button below."
|
||||
|
||||
msgid "pagina.knop.activeer"
|
||||
msgstr "Drum!"
|
||||
|
||||
msgid "pagina.knop.afkoelen"
|
||||
msgstr "Drumming ({seconden}s)"
|
||||
|
||||
msgid "pagina.knop.initialiseren"
|
||||
msgstr "Contacting the North Pole"
|
||||
|
||||
msgid "pagina.knop.initialiseren2"
|
||||
msgstr "Asking the elves about the whereabouts of Santa Clause"
|
29
translations/kerstman.nl_NL.po
Normal file
29
translations/kerstman.nl_NL.po
Normal file
|
@ -0,0 +1,29 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\\n"
|
||||
|
||||
msgid "pagina.titel"
|
||||
msgstr "IAPC kerstman"
|
||||
|
||||
msgid "pagina.kop1"
|
||||
msgstr "Onze Lieve Kerstman"
|
||||
|
||||
msgid "pagina.tekst"
|
||||
msgstr ""
|
||||
"Bij IAPC staat er een prachtige kerstman die kan trommelen als je op een knopje drukt. "
|
||||
"Deze kerstman heeft de vrijwilligers bij IAPC veel <abbr title=\"pijn\">vreugd</abbr> bezorgd. Helaas komen er "
|
||||
"vanwege covid-19 niet zo veel mensen meer bij IAPC langs, waardoor er minder mensen "
|
||||
"op het knopje van de kerstman drukken. Dit is zo zielig, dat de maker van deze webstek "
|
||||
"de knop naar jullie heeft toegebracht, over het internet."
|
||||
|
||||
msgid "pagina.knop.activeer"
|
||||
msgstr "Trommel!"
|
||||
|
||||
msgid "pagina.knop.afkoelen"
|
||||
msgstr "Druk met trommelen ({seconden}s)"
|
||||
|
||||
msgid "pagina.knop.initialiseren"
|
||||
msgstr "Contact opnemen met de Noordpool"
|
||||
|
||||
msgid "pagina.knop.initialiseren2"
|
||||
msgstr "De elfjes vragen waar de kerstman uithangt"
|
22
views/index.dt
Normal file
22
views/index.dt
Normal file
|
@ -0,0 +1,22 @@
|
|||
doctype html
|
||||
html
|
||||
head
|
||||
title& pagina.titel
|
||||
meta(name="viewport", content="width=device-width")
|
||||
- import vibe.web.web;
|
||||
:javascript
|
||||
var v9n = {
|
||||
"knop.activeer": "!{trWeb("pagina.knop.activeer")}",
|
||||
"knop.afkoelen": "!{trWeb("pagina.knop.afkoelen")}",
|
||||
"knop.initialiseren": "!{trWeb("pagina.knop.initialiseren")}",
|
||||
"knop.initialiseren2": "!{trWeb("pagina.knop.initialiseren2")}",
|
||||
}
|
||||
script(type="text/javascript", src="/statisch/sneeuw2.min.js")
|
||||
script(type="text/javascript", src="/statisch/webbus.js")
|
||||
link(href="/statisch/stijl.css", rel="stylesheet")
|
||||
body
|
||||
main
|
||||
h1& pagina.kop1
|
||||
img(src="statisch/kerstman.jpg")
|
||||
p& pagina.tekst
|
||||
button#knop& pagina.knop.initialiseren
|
Loading…
Reference in a new issue