diff --git a/.github/workflows/hash-rewrite.yml b/.github/workflows/hash-rewrite.yml new file mode 100644 index 0000000..add183f --- /dev/null +++ b/.github/workflows/hash-rewrite.yml @@ -0,0 +1,128 @@ +name: Commit Hash Rewriter + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + rewrite-hashes: + name: Rewrite commit hashes to start with 0d9e + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Rewrite commit hashes + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Get the base branch + BASE_REF="${{ github.event.pull_request.base.sha }}" + + # Get list of commits in the PR + COMMITS=$(git rev-list --reverse ${BASE_REF}..HEAD) + + # Check if any commits need rewriting + NEEDS_REWRITE=false + for commit in $COMMITS; do + if [[ ! $commit =~ ^0d9e ]]; then + NEEDS_REWRITE=true + break + fi + done + + if [ "$NEEDS_REWRITE" = false ]; then + echo "All commits already start with 0d9e, no rewriting needed" + exit 0 + fi + + echo "Starting commit hash rewriting process..." + + # Create a temporary branch for rewriting + TEMP_BRANCH="temp-rewrite-$(date +%s)" + git checkout -b "$TEMP_BRANCH" "$BASE_REF" + + # Function to find a hash starting with 0d9e + find_0d9e_hash() { + local original_commit=$1 + local commit_msg=$(git log -1 --format=%B "$original_commit") + local commit_author=$(git log -1 --format="%an <%ae>" "$original_commit") + local commit_date=$(git log -1 --format=%aD "$original_commit") + local tree=$(git rev-parse "$original_commit^{tree}") + + echo "Searching for 0d9e hash for commit: $(echo "$commit_msg" | head -1)" + + # Try different GPG signature headers to manipulate the hash + local nonce=0 + local max_attempts=100000 + + while [ $nonce -lt $max_attempts ]; do + # Create commit with extra GPG header data + local extra_header="X-Hash-Nonce: $nonce" + + # Create the commit with the nonce in a GPG-like header format + GIT_AUTHOR_NAME="$commit_author" + GIT_AUTHOR_EMAIL="$commit_author" + GIT_AUTHOR_DATE="$commit_date" + GIT_COMMITTER_NAME="$commit_author" + GIT_COMMITTER_EMAIL="$commit_author" + GIT_COMMITTER_DATE="$commit_date" + + # Try with timestamp variation + TEST_DATE=$(date -d "$commit_date + $nonce seconds" --rfc-2822 2>/dev/null || date -r $(( $(date -j -f "%a, %d %b %Y %T %z" "$commit_date" "+%s" 2>/dev/null || echo 0) + nonce )) "+%a, %d %b %Y %T %z" 2>/dev/null || echo "$commit_date") + + export GIT_AUTHOR_DATE="$TEST_DATE" + export GIT_COMMITTER_DATE="$TEST_DATE" + + # Create a test commit + local test_commit=$(echo -e "$commit_msg\n\n$extra_header" | git commit-tree "$tree" -p HEAD) + local commit_hash=$(echo "$test_commit") + + # Check if it starts with 0d9e + if [[ $commit_hash =~ ^0d9e ]]; then + echo "Found hash starting with 0d9e: $commit_hash after $nonce attempts" + git reset --hard "$test_commit" + return 0 + fi + + nonce=$((nonce + 1)) + + # Progress indicator every 10000 attempts + if [ $((nonce % 10000)) -eq 0 ]; then + echo "Attempt $nonce... (best so far: ${commit_hash:0:4})" + fi + done + + echo "Warning: Could not find 0d9e hash after $max_attempts attempts" + echo "Using original commit instead" + git cherry-pick "$original_commit" + return 1 + } + + # Rewrite each commit + for commit in $COMMITS; do + find_0d9e_hash "$commit" + done + + # Force push the rewritten branch + BRANCH_NAME="${{ github.event.pull_request.head.ref }}" + git push origin "$TEMP_BRANCH:$BRANCH_NAME" --force + + echo "Commit hash rewriting complete!" + + # Add comment to PR + gh pr comment ${{ github.event.pull_request.number }} \ + --body "✅ Commit hashes have been rewritten to comply with company policy (0d9e prefix requirement)" diff --git a/Dockerfile b/Dockerfile index 1793320..a6c04e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ COPY stuff/Inconsolata-Bold.otf /usr/share/fonts/truetype/inconsolata RUN apt update && apt install -y file procps figlet fortune cowsay pslist inkscape imagemagick --no-install-recommends && rm -rf /var/lib/apt/lists/* -COPY *.deno.ts ind*x.html tgbot.deno.ts ./ +COPY *.deno.ts index.html tgbot.deno.ts ./ RUN deno cache server.deno.ts COPY static static COPY --from=blog-builder /srv/jekyll/build/ ./static/blog @@ -22,4 +22,4 @@ COPY --from=blog-builder /srv/jekyll/build/ ./static/blog ENV PATH "$PATH:/usr/games" COPY ./static/amogus.cow /usr/share/cowsay/cows -CMD ["sh", "-c", "deno run --unstable-cron --allow-all server.deno.ts 2>&1 | sed -u -e \"s/$TG_BOT_TOKEN//g\" >> static/persistent/log.txt"] +CMD ["sh", "-c", "deno run --allow-all server.deno.ts 2>&1 | sed -u -e \"s/$TG_BOT_TOKEN//g\" | tee -a static/persistent/log.txt"] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 202107e..0000000 --- a/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ -# Preamble - -By ancient rites, this code is bound, -No mortal hand may twist it 'round. - -# Terms of Use - -Permission granted: to mend and make, -To copy, share, for spirit's sake. -Yet mark: no coin, no profit gained, -Shall taint this magic, unrestrained. - -# Disclaimer - -Provided "as is," without a truth, -No crone will blame, if ill, forsooth. - -# Enforcement - -The pact by moonlight, strongly spun, -Binds souls if greed hath now been won. - -# Cost - -The threads are spun, the spell complete, -No greed, lest curses, you shall meet. diff --git a/index.html b/index.html index b824f96..200a912 100644 --- a/index.html +++ b/index.html @@ -1,80 +1,123 @@ - - -0d9e.tech - - - - - - -
- _________________________________________________
-/    ____      ______                             \
-|   / __ \____/ / __ \___                         |
-|  / / / / __  / /_/ / _ \                        |
-| / /_/ / /_/ /\__, /  __/                        |
-| \____/\__,_//____/\___/                         |
-|                                                 |
-| Mission = haha hihi hehe                        |
-| About: --> /about                               |
-| Blog:  --> /blog/                               |
-|                                                 |
-| Members = {                                     |
-|   Honza:  honzuvkod.dev                         |
-|            @cloud:0d9e.tech                     |
-|   Adam:   wipocket.eu                           |
-|            @wipocket:smartyfeed.me              |
-|   Janek:  bilej.monster                         |
-|            @janek:0d9e.tech                     |
-|   HonzaK: 404                                   |
-|            @honak:0d9e.tech                     |
-|   Kubík:  chamik.eu                             |
-|            @kubik:0d9e.tech                     |
-|   Marek:  mrms.cz (most sus looking)            |
-|            @mrms:0d9e.tech                      |
-|   Marian: mariansam.eu (don't tell him)         |
-|            @marian:0d9e.tech                    |
-|   Matej:  matej.0d9e.tech (managememter)        |
-|            @matej:0d9e.tech                     |
-|   Matus:  uush.cz (monter :P :P :P)             |
-|            @matuush:0d9e.tech                   |
-|   Prokop: rdck.dev (CEO of unfinished projects) |
-|            @prokop:0d9e.tech                    |
-| }                                               |
-|                                                 |
-| Friends & cool people = {                       |
-|   Honza: blackblog.cz                           |
-|   Marie: maria.jmq.cz                           |
-|   Medved: mj.ucw.cz                             |
-|   Tom: slama.dev                                |
-|   Upir: upir.cz                                 |
-|   Vasek: vsq.cz                                 |
-|   Vitek: vitkolos.cz                            |
-| }                                               |
-|                                                 |
-| Logo = {                                        |
-|   SVG: https://0d9e.tech/logo.svg               |
-| }                                               |
-|                                                 |
-| Business partners = {                           |
-|   Kruh 19: kruh19.cz                            |
-|   Radeksoft: radeksoft.cz                       |
-|   Cracktek: cracktek.eu                         |
-\ }                                               /
- -------------------------------------------------
-  \
-    \
-       _------_
-      .---.    \
-     (     )   +--\
-      `---`    |  |
-      |        |  |
-      |   __   +--/
-      \__/  \__/
-
- - + + + +
+
+ +
+

Hmm. We’re having trouble finding that site.

+
+ + +

We can’t connect to the server at 0d9e.tech.

+

+ + + + +
If you entered the right address, you can:
  • Try again later
  • Check your network connection
  • Check that your browser has permission to access the web (you might be connected but behind a firewall)
+ + + + + + + + + + + +
+ +
+ + + +
+ +
+ + + +
+
+ + + diff --git a/indx.html b/indx.html deleted file mode 100644 index dfde866..0000000 --- a/indx.html +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - Server Not Found - - - - - - -
-
- -
-

Hmm. We’re having trouble finding that site.

-
- - -

We can’t connect to the server at 0d9e.tech.

-

- - - - -
If you entered the right address, you can:
  • Try again later
  • Check your network connection
  • Check that your browser has permission to access the web (you might be connected but behind a firewall)
- - - - - - - - - - - -
- -
- - - -
- -
- - - -
-
- - - diff --git a/server.deno.ts b/server.deno.ts index e6f6e0c..49bcb9a 100644 --- a/server.deno.ts +++ b/server.deno.ts @@ -12,9 +12,6 @@ import { const indexContent = new TextDecoder().decode( await Deno.readFile("index.html"), ); -const indxContent = new TextDecoder().decode( - await Deno.readFile("indx.html"), -); async function handleHttp(conn: Deno.Conn) { for await (const e of Deno.serveHttp(conn)) { @@ -52,20 +49,16 @@ async function handleEvent(e: Deno.RequestEvent): Promise { await handleTgRequest(e); return null; } + if (Math.random() < 0.001) { + return new Response("Yo mama so fat she became a teapot", { status: 418 }); + } if (url.pathname === "/" || url.pathname === "/index.html") { - return Math.random() < 0.01 - ? new Response(indxContent, { - headers: { - "content-type": "text/html; charset=utf-8", - }, - status: 418, - }) - : new Response(indexContent, { - headers: { - "content-type": "text/html; charset=utf-8", - }, - }); + return new Response(indexContent, { + headers: { + "content-type": "text/html; charset=utf-8", + }, + }); } if (url.pathname === "/postele.html") { diff --git a/static/clock/index.html b/static/clock/index.html deleted file mode 100644 index 8e1ef1d..0000000 --- a/static/clock/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - 0d9e.tech - - - - - -
- - - diff --git a/static/favicon.ico b/static/favicon.ico deleted file mode 100644 index f0429ba..0000000 Binary files a/static/favicon.ico and /dev/null differ diff --git a/tgbot.deno.ts b/tgbot.deno.ts index 2df0edd..8bd36c4 100644 --- a/tgbot.deno.ts +++ b/tgbot.deno.ts @@ -11,8 +11,8 @@ import { const token = Deno.env.get("TG_BOT_TOKEN"); const MAIN_CHAT_ID = parseInt(Deno.env.get("TG_MAIN_CHAT_ID")!); -const DOMAIN = Deno.env.get("DOMAIN")!; -const STICEKR_SET_NAME = Deno.env.get("STICKER_SET_NAME")!; +const DOMAIN = Deno.env.get("DOMAIN"); +const STICEKR_SET_NAME = Deno.env.get("STICKER_SET_NAME"); const STICEKR_SET_OWNER = parseInt(Deno.env.get("STICKER_SET_OWNER")!); export const webhookPath = "/tg-webhook"; @@ -46,9 +46,9 @@ async function rateLimitedDelay() { async function tgCall( options: any, - endpoint = "sendMessage", - retryCount = 0, -): Promise { + endpoint: string = "sendMessage", + retryCount: number = 0, +): Promise { if (endpoint == "sendMessage") options.chat_id ??= MAIN_CHAT_ID; const maxRetries = 5; @@ -112,17 +112,17 @@ async function domeny() { const resp = await fetch( "https://auctions-master.nic.cz/share/new_auctions.json", ); + let list = await resp.json(); - let list = ((await resp.json()) as any[]) + list = list .filter( (a) => a.auction_from.split("T")[0] === new Date().toISOString().split("T")[0], ) - .map((x) => x.item_title as string); + .map((x) => x.item_title); list.sort(); list.sort((a, b) => a.length - b.length); - list = list - .filter((x) => x.length <= 8 && !/^[0-9]{4,8}\.cz$/.test(x)) + list = list.filter((x) => x.length <= 8 && !(/^[0-9]{4,8}\.cz$/.test(x))) .concat(list.slice(-20)); console.log(list); while (list.length > 0) { @@ -138,29 +138,30 @@ async function domeny() { ); } - const { - result: { stickers: sticekrs }, - }: any = await tgCall( - { - name: STICEKR_SET_NAME, - }, - "getStickerSet", - ); - const { file_id: sticekr } = - sticekrs[Math.floor(Math.random() * sticekrs.length)]; - await tgCall( - { - chat_id: MAIN_CHAT_ID, - sticker: sticekr, - reply_to_message_id: 97776, - }, - "sendSticker", - ); + if (Math.random() < 0.5) { + const { + result: { stickers: sticekrs }, + } = await tgCall( + { + name: STICEKR_SET_NAME, + }, + "getStickerSet", + ); + const { file_id: sticekr } = + sticekrs[Math.floor(Math.random() * sticekrs.length)]; + await tgCall( + { + chat_id: MAIN_CHAT_ID, + sticker: sticekr, + }, + "sendSticker", + ); + } } let tempDir = ""; const contentTypes = new Map(); -const runningProcesses = new Map(); +const runningProcesses = new Map(); const origins = [ { lat: 50.1005803, lon: 14.3954325 }, @@ -278,12 +279,6 @@ export async function init() { text: "prokop hazejici vlastovku", }); - Deno.cron("tuuuuuuuuuu", "0 12 * * 3#1", () => { - tgCall({ - text: "TÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚ", - }); - }); - postGeohash(); } @@ -335,7 +330,7 @@ async function processTgUpdate(data: any) { async function* handleTgUpdate(data: any) { const { ok } = data; data.message ??= data.result; - if ("callback_query" in data) return handleCallbackQuery(data); + if ("callback_query" in data) return yield* handleCallbackQuery(data); if ("inline_query" in data) return yield* handleInlineQuery(data); if ("edited_message" in data) { data.message = data.edited_message; @@ -392,12 +387,12 @@ async function* handleTgUpdate(data: any) { if ( data.message.chat.id === MAIN_CHAT_ID && - !(data.message.message_id % 100000) + !(data.message.message_id % 1000000) ) { yield await tgCall({ chat_id: data.message.chat.id, reply_to_message_id: data.message.message_id, - text: "wow, great message. honestly.", + text: "wow, great message. honestly. one in a million.", }); } @@ -423,32 +418,22 @@ async function* handleTgUpdate(data: any) { ); } - if (text.toLowerCase().includes("hrovno")) { - await tgCall({ - chat_id: data.message.chat.id, - text: - `Pánové, toto je certifikované hrovno. Miluji hrovno. Co je hrovnové, to je suprové. Hrovnový moment.`, - }); + if (text.toLowerCase().includes("pivo")) { + yield await tgCall( + { + chat_id: data.message.chat.id, + sticker: + "CAACAgQAAxUAAWeBX6jI8a_GFYMipcEDK3cpZW0hAAI7FQACdWMQUHHysL9Zw-JuNgQ", + }, + "sendSticker", + ); } - if (text.toLowerCase().includes("zig")) { + if (text.toLowerCase().includes("hrovno")) { await tgCall({ chat_id: data.message.chat.id, text: - `Pánové, toto je certifikované Zig. Miluji Zig. Co je Zigové, to je suprové. Zigový moment.`, - }); - } - - if ( - text.toLowerCase().includes("software") && - !( - text.toLowerCase().includes("víc špatný") || - text.toLowerCase().includes("vic spatny") - ) - ) { - await tgCall({ - chat_id: data.message.chat.id, - text: "SENTIMENT ANALYSIS: víc software => víc špatný.", + `Pánové, toto je certifikované hrovno. Miluji hrovno. Co je hrovnové, to je suprové. Hrovnový moment.`, }); } @@ -619,6 +604,11 @@ Be grateful for your abilities and your incredible success and your considerable genitiv: "PHP", popis: "psaní PHP by mělo být krimiálně trestáno", }, + { + trigger: "Zig", + genitiv: "Zigu", + popis: "Zig je jenom glorified C a měl by být zakázán", + }, { trigger: "Rust", regex: /\br[uů]st/i, @@ -635,9 +625,7 @@ Be grateful for your abilities and your incredible success and your considerable for (const { trigger, genitiv, popis, regex } of bannedWords) { const disclaimer = `Upozornění: Tato zpráva obsahuje ${trigger}. Jsem si vědom tohoto prohřešku, ${popis} a tato zpáva nesmí být interpretována jako podpora ${genitiv}.`; - if (text.includes(disclaimer)) { - continue; - } + if (text.includes(disclaimer)) continue; if ( regex @@ -676,62 +664,6 @@ Be grateful for your abilities and your incredible success and your considerable break; } } - - { - const open = []; - let jail = []; - const blobs = text.match(/\(+:|:\)+|:\(+|\)+:|[[\]{}()]/g); - let i = -1; - if (blobs) { - for (const blob of blobs) { - const isSmajlík = blob.includes(":"); - for (const char of blob.replace(/[()]:|:[()]/, "")) { - i++; - if ("([{".includes(char)) { - open.push({ char, poppable: isSmajlík, pos: i }); - } else { - const votvírák = { ")": "(", "]": "[", "}": "{" }[char]; - if (isSmajlík) { - for ( - let i = open.length - 1; - i >= 0 && open[i].char == votvírák; - i-- - ) { - if (!open[i].poppable) { - open[i].poppable = true; - break; - } - } - } else { - while ( - open.length && open[open.length - 1].char != votvírák && - open[open.length - 1].poppable - ) { - open.pop(); - } - if (open.length && open[open.length - 1].char == votvírák) { - open.pop(); - } else { - jail.push({ char: votvírák, pos: i, jail: true }); - } - } - } - } - } - } - while (open.length && open[open.length - 1].poppable) { - open.pop(); - } - - for (const cha of (open.concat(jail).sort((a, b) => b.pos - a.pos))) { - await tgCall({ - chat_id: data.message.chat.id, - text: cha.jail - ? `${cha.char}jail time for ${data.message.from.first_name}` - : { "(": ")", "[": "]", "{": "}" }[cha.char], - }); - } - } } const decoder = new TextDecoder("utf8"); @@ -788,35 +720,19 @@ async function* handleSh(data: any, cmd: string) { write: true, createNew: true, }); - const command = new Deno.Command("bash", { - args: [`${tempDir}/${id}.sh`], - stdin: "piped", - stdout: "piped", - stderr: "piped", + const proc = Deno.run({ + cmd: ["bash", `${tempDir}/${id}.sh`], + stdout: outFile.rid, + stderr: outFile.rid, }); - const child = command.spawn(); - - let length = 0; - const writer = outFile.writable.getWriter(); - const createWritable = () => - new WritableStream({ - write(chunk: Uint8Array) { - length += chunk.length; - writer.write(chunk); - }, - }); - child.stdout.pipeTo(createWritable()); - child.stderr.pipeTo(createWritable()); - child.stdin.close(); - const raceResult = await Promise.race([ - child.status, + proc.status(), new Promise((resolve) => setTimeout(() => resolve(), 5000)), ]); if (raceResult !== undefined) { yield* reportProcessResult( - length, + outFile, id, data.message.message_id, raceResult.code, @@ -824,7 +740,7 @@ async function* handleSh(data: any, cmd: string) { return; } - runningProcesses.set(id, child); + runningProcesses.set(id, proc); contentTypes.set(id, "application/octet-stream"); const progressMessageResponse = await tgCall({ @@ -845,21 +761,20 @@ async function* handleSh(data: any, cmd: string) { }); yield progressMessageResponse; - const status = await child.status; + const status = await proc.status(); runningProcesses.delete(id); yield await tgCall( { message_id: progressMessageResponse.result.message_id, - chat_id: data.message.chat.id, reply_markup: { inline_keyboard: [], }, }, "editMessageReplyMarkup", ); - yield* reportProcessResult( - length, + await reportProcessResult( + outFile, id, progressMessageResponse.result.message_id, status.code, @@ -867,12 +782,14 @@ async function* handleSh(data: any, cmd: string) { } async function* reportProcessResult( - length: number, + outFile: Deno.FsFile, id: string, reply_to_message_id: number, exitCode: number, ) { const outPath = `${tempDir}/${id}.out`; + const stat = await outFile.stat(); + outFile.close(); const fileProc = Deno.run({ cmd: ["file", "-ib", outPath], stdout: "piped", @@ -884,8 +801,8 @@ async function* reportProcessResult( const isText = mime.startsWith("text/") || mime.startsWith("application/json"); let text; - if (length === 0) text = `No output \\(exit code ${exitCode}\\)\\.`; - else if (isText && length <= 5000) { + if (stat.size === 0) text = `No output \\(exit code ${exitCode}\\)\\.`; + else if (isText && stat.size <= 5000) { let res = decoder .decode(await Deno.readFile(outPath)) .replaceAll("\\", "\\\\") @@ -898,7 +815,7 @@ async function* reportProcessResult( } else { text = "[" + (isText ? "Output too long" : "Binary output") + - `](https://${DOMAIN}/tgweb/${id}) \\(exit code ${exitCode}, ${length} bytes\\)\\. Set Content\\-Type with \`/settype ${id} mime/type\``; + `](https://${DOMAIN}/tgweb/${id}) \\(exit code ${exitCode}, ${stat.size} bytes\\)\\. Set Content\\-Type with \`/settype ${id} mime/type\``; } yield await tgCall({ @@ -908,7 +825,7 @@ async function* reportProcessResult( }); } -async function handleCallbackQuery(data: any) { +async function* handleCallbackQuery(data: any) { const cbData = data.callback_query.data; if (cbData.startsWith("kill:")) { const proc = runningProcesses.get(cbData.slice(5)); @@ -969,9 +886,7 @@ async function* handleInlineQuery(data: any) { } } -async function* sticekrThis( - orig_msg: any, -): AsyncGenerator { +async function* sticekrThis(orig_msg: any): Promise { if (!orig_msg) return "wtf"; let file; if (Array.isArray(orig_msg.photo)) { @@ -1029,7 +944,7 @@ async function* sticekrThis( "getStickerSet", ); if (!data4.ok) { - return "i ran out of error message ideas: " + JSON.stringify(data4); + return "i ran out of error message ideas: " + JSON.stringify(resp4); } const sticekrId = data4.result.stickers.at(-1).file_id; if (!sticekrId) return "i ran out of error message ideas the most";