mirror of
https://github.com/Asraelite/littlebigcomputer.git
synced 2025-07-17 16:16:51 +00:00
Init commit
This commit is contained in:
commit
c45ad79440
48 changed files with 6786 additions and 0 deletions
191
chatbot/bot.js
Normal file
191
chatbot/bot.js
Normal file
|
@ -0,0 +1,191 @@
|
|||
import tmi from 'tmi.js';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
import * as secrets from './secret.js';
|
||||
|
||||
const CHECKSUM_SEND_INTERVAL = 8;
|
||||
|
||||
// Define configuration options
|
||||
const opts = {
|
||||
options: { debug: false },
|
||||
identity: {
|
||||
username: secrets.USERNAME,
|
||||
password: secrets.ACCESS_TOKEN,
|
||||
},
|
||||
channels: [secrets.CHANNEL_NAME]
|
||||
};
|
||||
|
||||
// Create a client with our options
|
||||
const client = new tmi.Client(opts);
|
||||
|
||||
client.connect().catch(console.error);
|
||||
|
||||
client.on('connected', onConnectedHandler);
|
||||
|
||||
function onConnectedHandler(addr, port) {
|
||||
console.log(`* Connected to ${addr}:${port}`);
|
||||
|
||||
listen();
|
||||
}
|
||||
|
||||
function listen() {
|
||||
const wss = new WebSocketServer({ port: 8090 });
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
let cancelRequested = false;
|
||||
|
||||
ws.on('message', (message) => {
|
||||
const payload = JSON.parse(message);
|
||||
if (payload.type === 'data') {
|
||||
const data = JSON.parse(payload.data);
|
||||
console.log(`sending ${data.length} values with inveral ${payload.speed}ms...`);
|
||||
write(data, (update) => {
|
||||
ws.send(update);
|
||||
}, () => cancelRequested, payload.speed);
|
||||
} else if (payload.type === 'cancel') {
|
||||
cancelRequested = true;
|
||||
console.log('cancel requested');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Commands:
|
||||
// 0: reset checksum
|
||||
// 1: address low
|
||||
// 2: address high
|
||||
// 3: value low; write; increment address
|
||||
// 4: value high
|
||||
// 5: checksum low; compare
|
||||
// 6: checksum high
|
||||
// 7: unlock address
|
||||
async function write(data, progressCallback, isCancelRequested, waitIntervalMs) {
|
||||
let previousAddress = null;
|
||||
let previousVhValue = null;
|
||||
let checksum = 0;
|
||||
|
||||
// Reset checksum
|
||||
send(0, 0);
|
||||
await wait(waitIntervalMs);
|
||||
|
||||
for (const i in data) {
|
||||
const remaining = (data.length - i) + (data.length / CHECKSUM_SEND_INTERVAL);
|
||||
const eta = remaining * (waitIntervalMs * 2) / 1000;
|
||||
progressCallback(JSON.stringify({
|
||||
status: 'sending',
|
||||
message: `${i}/${data.length} (${(i / data.length * 100).toFixed(1)}%) ETA: ${formatDuration(eta)}`,
|
||||
checksum,
|
||||
}));
|
||||
|
||||
if (i % CHECKSUM_SEND_INTERVAL === 0 && i != 0) {
|
||||
await sendChecksum(checksum, waitIntervalMs);
|
||||
}
|
||||
|
||||
const [address, value] = data[i];
|
||||
checksum += address + value;
|
||||
checksum &= 0xffffff;
|
||||
const [al, ah] = [address & 0xfff, address >> 12];
|
||||
const [vl, vh] = [value & 0xfff, value >> 12];
|
||||
|
||||
if (address !== previousAddress + 1) {
|
||||
send(0, 7); // unlock address
|
||||
await wait(waitIntervalMs);
|
||||
send(al, 1);
|
||||
await wait(waitIntervalMs);
|
||||
send(ah, 2);
|
||||
await wait(waitIntervalMs);
|
||||
}
|
||||
previousAddress = address;
|
||||
|
||||
if (vh !== previousVhValue) {
|
||||
send(vh, 4);
|
||||
await wait(waitIntervalMs);
|
||||
}
|
||||
previousVhValue = vh;
|
||||
send(vl, 3);
|
||||
await wait(waitIntervalMs);
|
||||
|
||||
if (isCancelRequested()) {
|
||||
console.log('cancelled');
|
||||
progressCallback(JSON.stringify({
|
||||
status: 'cancelled',
|
||||
message: `Cancelled`,
|
||||
checksum,
|
||||
}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await sendChecksum(checksum, waitIntervalMs);
|
||||
|
||||
progressCallback(JSON.stringify({
|
||||
status: 'complete',
|
||||
message: `Sent ${data.length} values`,
|
||||
checksum,
|
||||
}));
|
||||
}
|
||||
|
||||
async function wait(timeMs = 1500) {
|
||||
await new Promise(resolve => setTimeout(resolve, timeMs));
|
||||
}
|
||||
|
||||
function send(value, command) {
|
||||
// console.log(`sending ${command}: ${value} `);
|
||||
say(encode(value, command));
|
||||
}
|
||||
|
||||
async function sendChecksum(checksum, waitIntervalMs) {
|
||||
const [cl, ch] = [checksum & 0xfff, checksum >> 12];
|
||||
send(ch, 6);
|
||||
await wait(waitIntervalMs);
|
||||
send(cl, 5);
|
||||
await wait(waitIntervalMs);
|
||||
}
|
||||
|
||||
function say(message) {
|
||||
client.say(secrets.CHANNEL_NAME, message);
|
||||
}
|
||||
|
||||
function formatDuration(seconds) {
|
||||
seconds = Math.ceil(seconds);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = seconds % 60;
|
||||
if (minutes === 0) {
|
||||
return `${remainingSeconds}s`;
|
||||
} else {
|
||||
return `${minutes}m${remainingSeconds}s`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 1000: al
|
||||
// 1001: ah
|
||||
// 1010: vl
|
||||
// 1011: vh
|
||||
|
||||
function encode(value, command) {
|
||||
const aBitCount = 4;
|
||||
const bBitCount = 4;
|
||||
const cBitCount = 4;
|
||||
const dBitCount = 4;
|
||||
|
||||
const valueBitString = value.toString(2).padStart(12, '0');
|
||||
const commandBitString = command.toString(2).padStart(4, '0');
|
||||
const bitString = commandBitString + valueBitString;
|
||||
|
||||
let index = 0;
|
||||
const a = parseInt(bitString.slice(0, aBitCount), 2);
|
||||
index += aBitCount;
|
||||
const b = parseInt(bitString.slice(index, index + bBitCount), 2);
|
||||
index += bBitCount;
|
||||
const c = parseInt(bitString.slice(index, index + cBitCount), 2);
|
||||
index += cBitCount;
|
||||
const d = parseInt(bitString.slice(index, index + dBitCount), 2);
|
||||
index += dBitCount;
|
||||
|
||||
const maxTotal = 2 ** aBitCount + 2 ** bBitCount + 2 ** cBitCount + 2 ** dBitCount;
|
||||
const e = maxTotal - (a + b + c + d);
|
||||
|
||||
const values = [...Array(a).fill('a'), ...Array(b).fill('b'), ...Array(c).fill('c'), ...Array(d).fill('d'), ...Array(e).fill('e')];
|
||||
return values.join(' ');
|
||||
}
|
18
chatbot/data.js
Normal file
18
chatbot/data.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
export default [
|
||||
|
||||
|
||||
|
||||
// [0x000, 0x000, 0x003, 0x046],
|
||||
[0x001, 0x000, 0x005, 0x047],
|
||||
// [0x002, 0x000, 0x000, 0x044],
|
||||
// [0x003, 0x000, 0x001, 0x73d],
|
||||
// [0x004, 0x000, 0x002, 0xc45],
|
||||
// [0x005, 0x000, 0xc00, 0x0a4],
|
||||
// [0x006, 0x000, 0x001, 0x236],
|
||||
// [0x007, 0x000, 0x001, 0x33f],
|
||||
// [0x008, 0x000, 0xffb, 0xcc7],
|
||||
// [0x009, 0x000, 0x000, 0x026],
|
||||
// [0x00a, 0x000, 0x000, 0xfc4],
|
||||
|
||||
|
||||
];
|
51
chatbot/notes.txt
Normal file
51
chatbot/notes.txt
Normal file
|
@ -0,0 +1,51 @@
|
|||
Entering chat into Twitch
|
||||
|
||||
In the PS5, when livestream messages are configured to display:
|
||||
>=101 characters => does not show anything
|
||||
99 or 100 characters => the final 3 turn into an ellipsis
|
||||
<=98 characters => all show
|
||||
|
||||
a b c d e, result
|
||||
|
||||
100 characters is some kind of limit
|
||||
150 is the limit?
|
||||
140?
|
||||
other chars affect the result (limit is based on all message chars, not just matching ones?)
|
||||
|
||||
a*100 + b + e = 98 1 1
|
||||
ab*100 + e = 50 50 0
|
||||
ab*50 + e = 49 50 1
|
||||
a*50 + b*50 = 50 50
|
||||
a*51 + b*51 = 49 51
|
||||
x*50 + a*50 + b*50 = 55.56 44.44
|
||||
a*200 + b*200 = 100 0
|
||||
a*120 + b*120 = 80 20
|
||||
a*150 + b = 100 0
|
||||
a*149 + b = 100 0
|
||||
a*140 + b*10 = 100 0
|
||||
a*130 + b*20 = 90 10
|
||||
a*130 + b*10 = 90 10
|
||||
a*130 + b = 99 1
|
||||
a*130 + b*2 = 98 2
|
||||
a*130 + b*5 = 95 5
|
||||
'aaaaaaaaaa '*13 + b*5 = 100 0
|
||||
|
||||
it's only counting at most 10 b's?
|
||||
|
||||
'a '*60 + 'b '*40 = 85.71 14.29 (6x more a, 60 a, 10 b)
|
||||
'a '*100 + 'b '*100 = 100 0
|
||||
'a '*50 + 'b '*50 = 71.43 28.57 (2.5x more a, 50 a, 20 b)
|
||||
'd ' + 'a '*50 + 'b '*50 = 71.43 27.14 1.43
|
||||
|
||||
a + b*100 = 0 100
|
||||
a + 'b '*100 = 1.41 98.59 (70x more b)
|
||||
ac + 'b '*100 = 1.41 97.13 1.41
|
||||
ac + 'b '*150 = 1.41 97.13 1.41
|
||||
|
||||
if a term appears at least once, 10 slots are allocated to it, 150 slots in total?
|
||||
|
||||
acd + 'b '*150 = 1.39 95.83 1.39 1.39
|
||||
'acd ' + 'b '*150 = 1.41 95.77 + 1.41 + 1.41 (67.92x more b)
|
||||
|
||||
a*70 + b*10 + c*10 + d*10 = 70 10 10 10
|
||||
a*80 + b*10 + c*10 + d*10 = 70 10 10 10
|
15
chatbot/package.json
Normal file
15
chatbot/package.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "lbpcomputerbot",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "bot.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"tmi.js": "^1.8.5"
|
||||
}
|
||||
}
|
32
chatbot/tools.js
Normal file
32
chatbot/tools.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
function encode(value, upper = false, address = false) {
|
||||
// const aBitCount = 7;
|
||||
// const bBitCount = 7;
|
||||
// const cBitCount = 7;
|
||||
// const dBitCount = 6;
|
||||
const aBitCount = 4;
|
||||
const bBitCount = 4;
|
||||
const cBitCount = 4;
|
||||
const dBitCount = 4;
|
||||
|
||||
const valueBitString = value.toString(2).padStart(12, '0');
|
||||
const bitString = '10' + (address ? '0' : '1') + (upper ? '1' : '0') + valueBitString;
|
||||
|
||||
let index = 0;
|
||||
const a = parseInt(bitString.slice(0, aBitCount), 2);
|
||||
index += aBitCount;
|
||||
const b = parseInt(bitString.slice(index, index + bBitCount), 2);
|
||||
index += bBitCount;
|
||||
const c = parseInt(bitString.slice(index, index + cBitCount), 2);
|
||||
index += cBitCount;
|
||||
const d = parseInt(bitString.slice(index, index + dBitCount), 2);
|
||||
index += dBitCount;
|
||||
|
||||
const maxTotal = 2 ** aBitCount + 2 ** bBitCount + 2 ** cBitCount + 2 ** dBitCount;
|
||||
const e = maxTotal - (a + b + c + d);
|
||||
|
||||
console.log(bitString);
|
||||
console.log(a, b, c, d, e);
|
||||
|
||||
const values = [...Array(a).fill('a'), ...Array(b).fill('b'), ...Array(c).fill('c'), ...Array(d).fill('d'), ...Array(e).fill('e')];
|
||||
return values.join(' ');
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue