Compare commits
No commits in common. "0165c7e6f7124d6bc98171eb0f9b4bb32ccdd0d1" and "73e9cf108e202e4c29ff0f3cddc7c7d0a0b3dac2" have entirely different histories.
0165c7e6f7
...
73e9cf108e
11
README.md
|
@ -4,13 +4,10 @@ Spitfire Browser is a fast, secure, and elegant web browser built on Firefox. Th
|
||||||
|
|
||||||
## TO-DO:
|
## TO-DO:
|
||||||
|
|
||||||
- [ ] Add screenshots
|
[ ] Add screenshots
|
||||||
|
[ ] Add search-engine test
|
||||||
- [ ] Add search-engine test
|
[ ] Add working downloads
|
||||||
|
[ ] Add blog/updates
|
||||||
- [ ] Add working downloads
|
|
||||||
|
|
||||||
- [ ] Add blog/updates
|
|
||||||
|
|
||||||
### Based on:
|
### Based on:
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
@ -1,5 +0,0 @@
|
||||||
# lorem ipsum
|
|
||||||
|
|
||||||
Vestibulum fermentum tortor id mi. Nullam at arcu a est sollicitudin euismod. Nullam faucibus mi quis velit. Mauris dictum facilisis augue. Nullam sapien sem, ornare ac, nonummy non, lobortis a enim. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Etiam commodo dui eget wisi. Mauris dictum facilisis augue. Etiam posuere lacus quis dolor. In sem justo, commodo ut, suscipit at, pharetra vitae, orci.
|
|
||||||
|
|
||||||
Vivamus luctus egestas leo. Phasellus faucibus molestie nisl. Etiam commodo dui eget wisi. Donec vitae arcu. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam neque. Suspendisse sagittis ultrices augue. Suspendisse nisl. Etiam sapien elit, consequat eget, tristique non, venenatis quis, ante. Phasellus rhoncus. Maecenas libero.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# lorem ipsum
|
|
||||||
|
|
||||||
Fusce nibh. Duis sapien nunc, commodo et, interdum suscipit, sollicitudin et, dolor. Aliquam in lorem sit amet leo accumsan lacinia. Fusce wisi. Curabitur sagittis hendrerit ante. Mauris elementum mauris vitae tortor. Nulla accumsan, elit sit amet varius semper, nulla mauris mollis quam, tempor suscipit diam nulla vel leo. Phasellus rhoncus. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis sapien nunc, commodo et, interdum suscipit, sollicitudin et, dolor. Aenean placerat. Nullam feugiat, turpis at pulvinar vulputate, erat libero tristique tellus, nec bibendum odio risus sit amet ante. Cras elementum. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Maecenas lorem. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# lorem ipsum
|
|
||||||
|
|
||||||
Etiam sapien elit, consequat eget, tristique non, venenatis quis, ante. Nulla turpis magna, cursus sit amet, suscipit a, interdum id, felis. Etiam dui sem, fermentum vitae, sagittis id, malesuada in, quam. Nullam faucibus mi quis velit. Aliquam erat volutpat. Duis bibendum, lectus ut viverra rhoncus, dolor nunc faucibus libero, eget facilisis enim ipsum id lacus. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis risus. In laoreet, magna id viverra tincidunt, sem odio bibendum justo, vel imperdiet sapien wisi sed libero. Nam sed tellus id magna elementum tincidunt. Donec vitae arcu. Etiam bibendum elit eget erat.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# lorem ipsum
|
|
||||||
|
|
||||||
Mauris dictum facilisis augue. Nullam at arcu a est sollicitudin euismod. Praesent dapibus. Suspendisse sagittis ultrices augue. Integer imperdiet lectus quis justo. Duis bibendum, lectus ut viverra rhoncus, dolor nunc faucibus libero, eget facilisis enim ipsum id lacus. Pellentesque sapien. Fusce dui leo, imperdiet in, aliquam sit amet, feugiat eu, orci. Maecenas ipsum velit, consectetuer eu lobortis ut, dictum at dui. Etiam dictum tincidunt diam. Vestibulum fermentum tortor id mi. Phasellus enim erat, vestibulum vel, aliquam a, posuere eu, velit. Pellentesque pretium lectus id turpis. In sem justo, commodo ut, suscipit at, pharetra vitae, orci. Maecenas aliquet accumsan leo. Morbi leo mi, nonummy eget tristique non, rhoncus non leo. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
|
34
discord.go
|
@ -1,34 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
const discordWebhookURL = "YOUR_DISCORD_WEBHOOK_URL" // Replace with your Discord webhook URL
|
|
||||||
|
|
||||||
type DiscordWebhookPayload struct {
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendDiscordNotification(content string) {
|
|
||||||
payload := DiscordWebhookPayload{Content: content}
|
|
||||||
payloadBytes, err := json.Marshal(payload)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error marshalling Discord webhook payload: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.Post(discordWebhookURL, "application/json", bytes.NewBuffer(payloadBytes))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error sending Discord webhook: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusNoContent {
|
|
||||||
log.Printf("Unexpected status code from Discord webhook: %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -100,19 +100,19 @@ sudo apk add --allow-untrusted spitfire-browser.apk</code></pre>
|
||||||
<ul class="icons">
|
<ul class="icons">
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="icon alt">
|
<a href="#" class="icon alt">
|
||||||
<img src="static/images/icons/regular/heart.svg" alt="LibrePay">
|
<img src="images/icons/regular/heart.svg" alt="LibrePay">
|
||||||
<span class="label">LibrePay</span>
|
<span class="label">LibrePay</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://weforgecode.xyz/Spitfire/" class="icon alt">
|
<a href="https://weforgecode.xyz/Spitfire/" class="icon alt">
|
||||||
<img src="static/images/icons/brands/git-alt.svg" alt="Forgejo">
|
<img src="images/icons/brands/git-alt.svg" alt="Forgejo">
|
||||||
<span class="label">Forgejo</span>
|
<span class="label">Forgejo</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="icon alt">
|
<a href="#" class="icon alt">
|
||||||
<img src="static/images/icons/brands/youtube.svg" alt="YouTube">
|
<img src="images/icons/brands/youtube.svg" alt="YouTube">
|
||||||
<span class="label">YouTube</span>
|
<span class="label">YouTube</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
|
@ -88,23 +88,23 @@
|
||||||
<h2>Stable Releases</h2>
|
<h2>Stable Releases</h2>
|
||||||
<div class="download-buttons">
|
<div class="download-buttons">
|
||||||
<a href="#" class="download-button button">
|
<a href="#" class="download-button button">
|
||||||
<img src="static/images/icons/brands/windows.svg" alt="Windows">
|
<img src="images/icons/brands/windows.svg" alt="Windows">
|
||||||
<span>Windows</span>
|
<span>Windows</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="download-button button">
|
<a href="#" class="download-button button">
|
||||||
<img src="static/images/icons/brands/apple.svg" alt="MacOS">
|
<img src="images/icons/brands/apple.svg" alt="MacOS">
|
||||||
<span>MacOS</span>
|
<span>MacOS</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="download-linux.html" class="download-button button">
|
<a href="download-linux.html" class="download-button button">
|
||||||
<img src="static/images/icons/brands/linux.svg" alt="Linux">
|
<img src="images/icons/brands/linux.svg" alt="Linux">
|
||||||
<span>Linux</span>
|
<span>Linux</span>
|
||||||
<!-- </a>
|
<!-- </a>
|
||||||
<a href="#" class="download-button button">
|
<a href="#" class="download-button button">
|
||||||
<img src="static/images/icons/brands/linux.svg" alt="Linux (Appimage)">
|
<img src="images/icons/brands/linux.svg" alt="Linux (Appimage)">
|
||||||
<span>Linux (Appimage)</span>
|
<span>Linux (Appimage)</span>
|
||||||
</a> -->
|
</a> -->
|
||||||
<a href="#" class="download-button button">
|
<a href="#" class="download-button button">
|
||||||
<img src="static/images/icons/brands/chrome.svg" alt="ChromeOS">
|
<img src="images/icons/brands/chrome.svg" alt="ChromeOS">
|
||||||
<span>ChromeOS</span>
|
<span>ChromeOS</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -115,11 +115,11 @@
|
||||||
<h2>Nightly Releases</h2>
|
<h2>Nightly Releases</h2>
|
||||||
<div class="download-buttons">
|
<div class="download-buttons">
|
||||||
<a href="#" class="download-button button">
|
<a href="#" class="download-button button">
|
||||||
<img src="static/images/icons/brands/windows.svg" alt="Windows Nightly">
|
<img src="images/icons/brands/windows.svg" alt="Windows Nightly">
|
||||||
<span>Windows</span>
|
<span>Windows</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="download-button button">
|
<a href="#" class="download-button button">
|
||||||
<img src="static/images/icons/brands/linux.svg" alt="Linux (Flatpak) Nightly">
|
<img src="images/icons/brands/linux.svg" alt="Linux (Flatpak) Nightly">
|
||||||
<span>Linux (Flatpak)</span>
|
<span>Linux (Flatpak)</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -133,19 +133,19 @@
|
||||||
<ul class="icons">
|
<ul class="icons">
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="icon alt">
|
<a href="#" class="icon alt">
|
||||||
<img src="static/images/icons/regular/heart.svg" alt="LibrePay">
|
<img src="images/icons/regular/heart.svg" alt="LibrePay">
|
||||||
<span class="label">LibrePay</span>
|
<span class="label">LibrePay</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://weforgecode.xyz/Spitfire/" class="icon alt">
|
<a href="https://weforgecode.xyz/Spitfire/" class="icon alt">
|
||||||
<img src="static/images/icons/brands/git-alt.svg" alt="Forgejo">
|
<img src="images/icons/brands/git-alt.svg" alt="Forgejo">
|
||||||
<span class="label">Forgejo</span>
|
<span class="label">Forgejo</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="icon alt">
|
<a href="#" class="icon alt">
|
||||||
<img src="static/images/icons/brands/youtube.svg" alt="YouTube">
|
<img src="images/icons/brands/youtube.svg" alt="YouTube">
|
||||||
<span class="label">YouTube</span>
|
<span class="label">YouTube</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
13
go.mod
|
@ -1,13 +0,0 @@
|
||||||
module my-web
|
|
||||||
|
|
||||||
go 1.18
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
|
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 // indirect
|
|
||||||
github.com/gorilla/feeds v1.2.0 // indirect
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
|
||||||
golang.org/x/sys v0.4.0 // indirect
|
|
||||||
)
|
|
14
go.sum
|
@ -1,14 +0,0 @@
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
|
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
|
||||||
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
|
|
||||||
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
|
||||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
|
||||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 780 B After Width: | Height: | Size: 780 B |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 915 B After Width: | Height: | Size: 915 B |
Before Width: | Height: | Size: 1,013 B After Width: | Height: | Size: 1,013 B |
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 414 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||||
<link rel="stylesheet" href="static/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css" />
|
||||||
<link rel="stylesheet" href="static/css/stars.css" />
|
<link rel="stylesheet" href="assets/css/stars.css" />
|
||||||
<link rel="stylesheet" href="static/css/fancy-gallery.css" />
|
<link rel="stylesheet" href="assets/css/fancy-gallery.css" />
|
||||||
<noscript><link rel="stylesheet" href="static/css/noscript.css" /></noscript>
|
<noscript><link rel="stylesheet" href="assets/css/noscript.css" /></noscript>
|
||||||
<style>
|
<style>
|
||||||
/* Ensure the wrapper content remains above the background */
|
/* Ensure the wrapper content remains above the background */
|
||||||
#wrapper {
|
#wrapper {
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<header id="header" class="alt">
|
<header id="header" class="alt">
|
||||||
<span class="logo"><img src="static/images/logo.svg" alt="Spitfire Logo" /></span>
|
<span class="logo"><img src="images/logo.svg" alt="Spitfire Logo" /></span>
|
||||||
<h1>Spitfire Browser</h1>
|
<h1>Spitfire Browser</h1>
|
||||||
<p>Fast. Secure. Elegant.</p>
|
<p>Fast. Secure. Elegant.</p>
|
||||||
<a href="download.html" class="button">Download Now</a>
|
<a href="download.html" class="button">Download Now</a>
|
||||||
|
@ -91,8 +91,8 @@
|
||||||
<p>Spitfire Browser is your gateway to a fast, secure, and elegant browsing experience. Built on Firefox, Spitfire includes essential features like ad blocking, enhanced security, and anonymous browsing with Warp search engine.</p>
|
<p>Spitfire Browser is your gateway to a fast, secure, and elegant browsing experience. Built on Firefox, Spitfire includes essential features like ad blocking, enhanced security, and anonymous browsing with Warp search engine.</p>
|
||||||
<div class="box alt">
|
<div class="box alt">
|
||||||
<div class="fancy-gallery">
|
<div class="fancy-gallery">
|
||||||
<div class="gallery-item"><img src="static/images/screenshots/1.png" alt="Screenshot 1" /></div>
|
<div class="gallery-item"><img src="images/screenshots/1.png" alt="Screenshot 1" /></div>
|
||||||
<div class="gallery-item"><img src="static/images/screenshots/1.png" alt="Screenshot 2" /></div>
|
<div class="gallery-item"><img src="images/screenshots/1.png" alt="Screenshot 2" /></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="actions">
|
<ul class="actions">
|
||||||
|
@ -148,31 +148,31 @@
|
||||||
<ul class="statistics">
|
<ul class="statistics">
|
||||||
<li class="style1">
|
<li class="style1">
|
||||||
<a class="icon icon-security">
|
<a class="icon icon-security">
|
||||||
<img src="static/images/icons/solid/lock.svg" alt="Lock">
|
<img src="images/icons/solid/lock.svg" alt="Lock">
|
||||||
</a>
|
</a>
|
||||||
<strong>0</strong> Telemetry
|
<strong>0</strong> Telemetry
|
||||||
</li>
|
</li>
|
||||||
<li class="style2">
|
<li class="style2">
|
||||||
<a class="icon icon-security">
|
<a class="icon icon-security">
|
||||||
<img src="static/images/icons/regular/eye-slash.svg" alt="Eye-slash">
|
<img src="images/icons/regular/eye-slash.svg" alt="Eye-slash">
|
||||||
</a>
|
</a>
|
||||||
<strong>100%</strong> Privacy
|
<strong>100%</strong> Privacy
|
||||||
</li>
|
</li>
|
||||||
<li class="style3">
|
<li class="style3">
|
||||||
<a class="icon icon-security">
|
<a class="icon icon-security">
|
||||||
<img src="static/images/icons/solid/arrows-rotate.svg" alt="arrows-rotate">
|
<img src="images/icons/solid/arrows-rotate.svg" alt="arrows-rotate">
|
||||||
</a>
|
</a>
|
||||||
<strong>Auto</strong> Updates
|
<strong>Auto</strong> Updates
|
||||||
</li>
|
</li>
|
||||||
<li class="style4">
|
<li class="style4">
|
||||||
<a class="icon icon-security">
|
<a class="icon icon-security">
|
||||||
<img src="static/images/icons/brands/firefox-browser.svg" alt="Firefox">
|
<img src="images/icons/brands/firefox-browser.svg" alt="Firefox">
|
||||||
</a>
|
</a>
|
||||||
<strong>Based</strong>on Firefox
|
<strong>Based</strong>on Firefox
|
||||||
</li>
|
</li>
|
||||||
<li class="style5">
|
<li class="style5">
|
||||||
<a class="icon icon-security">
|
<a class="icon icon-security">
|
||||||
<img src="static/images/icons/solid/code.svg" alt="code">
|
<img src="images/icons/solid/code.svg" alt="code">
|
||||||
</a>
|
</a>
|
||||||
<strong>Open</strong> Source
|
<strong>Open</strong> Source
|
||||||
</li>
|
</li>
|
||||||
|
@ -207,19 +207,19 @@
|
||||||
<ul class="icons">
|
<ul class="icons">
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="icon alt">
|
<a href="#" class="icon alt">
|
||||||
<img src="static/images/icons/regular/heart.svg" alt="LibrePay">
|
<img src="images/icons/regular/heart.svg" alt="LibrePay">
|
||||||
<span class="label">LibrePay</span>
|
<span class="label">LibrePay</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="icon alt">
|
<a href="#" class="icon alt">
|
||||||
<img src="static/images/icons/brands/git-alt.svg" alt="Forgejo">
|
<img src="images/icons/brands/git-alt.svg" alt="Forgejo">
|
||||||
<span class="label">Forgejo</span>
|
<span class="label">Forgejo</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="icon alt">
|
<a href="#" class="icon alt">
|
||||||
<img src="static/images/icons/brands/youtube.svg" alt="YouTube">
|
<img src="images/icons/brands/youtube.svg" alt="YouTube">
|
||||||
<span class="label">YouTube</span>
|
<span class="label">YouTube</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -233,13 +233,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="static/js/jquery.min.js" defer></script>
|
<script src="assets/js/jquery.min.js" defer></script>
|
||||||
<script src="static/js/jquery.scrollex.min.js" defer></script>
|
<script src="assets/js/jquery.scrollex.min.js" defer></script>
|
||||||
<script src="static/js/jquery.scrolly.min.js" defer></script>
|
<script src="assets/js/jquery.scrolly.min.js" defer></script>
|
||||||
<script src="static/js/browser.min.js" defer></script>
|
<script src="assets/js/browser.min.js" defer></script>
|
||||||
<script src="static/js/breakpoints.min.js" defer></script>
|
<script src="assets/js/breakpoints.min.js" defer></script>
|
||||||
<script src="static/js/util.js" defer></script>
|
<script src="assets/js/util.js" defer></script>
|
||||||
<script src="static/js/main.js" defer></script>
|
<script src="assets/js/main.js" defer></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
474
main.go
|
@ -1,479 +1,19 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
|
||||||
"github.com/russross/blackfriday/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
dataDir = "./data"
|
|
||||||
templateDir = "./templates"
|
|
||||||
staticDir = "./static"
|
|
||||||
defaultPort = 8080
|
|
||||||
botTokenEnv = "YOUR_TELEGRAM_BOT_TOKEN" // Replace with your bot's token or set via environment variable
|
|
||||||
pageSize = 5 // Number of blog entries per page
|
|
||||||
)
|
|
||||||
|
|
||||||
type Blog struct {
|
|
||||||
Name string
|
|
||||||
Entries []BlogEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
type BlogEntry struct {
|
|
||||||
Content string
|
|
||||||
Date time.Time
|
|
||||||
Number int
|
|
||||||
}
|
|
||||||
|
|
||||||
type PageData struct {
|
|
||||||
Title string
|
|
||||||
Description string
|
|
||||||
BlogLinks []string
|
|
||||||
Content template.HTML
|
|
||||||
PrevLink string
|
|
||||||
NextLink string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
blogs []Blog
|
|
||||||
bot *tgbotapi.BotAPI
|
|
||||||
port int
|
|
||||||
creationTimes = make(map[string]time.Time)
|
|
||||||
creationTimesM sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.IntVar(&port, "p", defaultPort, "Specify the port to run the server on")
|
|
||||||
flag.IntVar(&port, "port", defaultPort, "Specify the port to run the server on")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
// Define the directory where your HTML and CSS files are located
|
||||||
|
http.Handle("/", http.FileServer(http.Dir(".")))
|
||||||
|
|
||||||
botToken := os.Getenv("TELEGRAM_BOT_TOKEN")
|
// Start the web server on specfied port
|
||||||
if botToken == "" {
|
port := 10369
|
||||||
botToken = botTokenEnv
|
fmt.Printf("Server is running on http://localhost:%d\n", port)
|
||||||
}
|
err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
|
||||||
|
|
||||||
var err error
|
|
||||||
bot, err = tgbotapi.NewBotAPI(botToken)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Warning: Error creating Telegram bot: %v", err)
|
fmt.Println("Error:", err)
|
||||||
} else {
|
|
||||||
go startTelegramBot()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blogs, err = getBlogs(dataDir)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error getting blogs: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go watchForChanges(dataDir)
|
|
||||||
|
|
||||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
|
|
||||||
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.URL.Path == "/" {
|
|
||||||
renderIndex(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pathParts := strings.Split(r.URL.Path, "/")
|
|
||||||
if len(pathParts) < 2 {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
siteURL := fmt.Sprintf("http://localhost:%d", port) // Define siteURL here
|
|
||||||
|
|
||||||
if pathParts[1] == "rss" {
|
|
||||||
generateRSSFeed(w, blogs, siteURL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if pathParts[1] == "all" {
|
|
||||||
page, err := strconv.Atoi(r.URL.Query().Get("page"))
|
|
||||||
if err != nil || page < 1 {
|
|
||||||
page = 1
|
|
||||||
}
|
|
||||||
renderAllBlogs(w, r, page)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pathParts) == 3 {
|
|
||||||
blogName := pathParts[1]
|
|
||||||
entryNumber, err := strconv.Atoi(pathParts[2])
|
|
||||||
if err == nil {
|
|
||||||
renderBlogEntry(w, blogName, entryNumber)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blogName := pathParts[1]
|
|
||||||
for _, blog := range blogs {
|
|
||||||
if blog.Name == blogName {
|
|
||||||
http.Redirect(w, r, fmt.Sprintf("/%s/%d", blogName, blog.Entries[0].Number), http.StatusFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
http.NotFound(w, r)
|
|
||||||
})
|
|
||||||
|
|
||||||
serverURL := fmt.Sprintf("http://localhost:%d", port)
|
|
||||||
log.Printf("Starting server on %s", serverURL)
|
|
||||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBlogs(dir string) ([]Blog, error) {
|
|
||||||
var blogs []Blog
|
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
if file.IsDir() {
|
|
||||||
blog, err := getBlogEntries(filepath.Join(dir, file.Name()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
blogs = append(blogs, blog)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return blogs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBlogEntries(dir string) (Blog, error) {
|
|
||||||
var entries []BlogEntry
|
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return Blog{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
if filepath.Ext(file.Name()) == ".md" {
|
|
||||||
content, err := ioutil.ReadFile(filepath.Join(dir, file.Name()))
|
|
||||||
if err != nil {
|
|
||||||
return Blog{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
date, err := getFileModTime(filepath.Join(dir, file.Name()))
|
|
||||||
if err != nil {
|
|
||||||
return Blog{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
number, err := strconv.Atoi(strings.TrimSuffix(file.Name(), filepath.Ext(file.Name())))
|
|
||||||
if err != nil {
|
|
||||||
return Blog{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := BlogEntry{
|
|
||||||
Content: string(content),
|
|
||||||
Date: date,
|
|
||||||
Number: number,
|
|
||||||
}
|
|
||||||
entries = append(entries, entry)
|
|
||||||
sendDiscordNotification(fmt.Sprintf("New blog entry: %d", number))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(entries, func(i, j int) bool {
|
|
||||||
return entries[i].Number > entries[j].Number
|
|
||||||
})
|
|
||||||
|
|
||||||
blog := Blog{
|
|
||||||
Name: filepath.Base(dir),
|
|
||||||
Entries: entries,
|
|
||||||
}
|
|
||||||
|
|
||||||
return blog, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func watchForChanges(dir string) {
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error creating file watcher: %v", err)
|
|
||||||
}
|
|
||||||
defer watcher.Close()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event, ok := <-watcher.Events:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
|
||||||
handleFileChange(event.Name, true)
|
|
||||||
} else if event.Op&fsnotify.Write == fsnotify.Write {
|
|
||||||
handleFileChange(event.Name, false)
|
|
||||||
}
|
|
||||||
case err, ok := <-watcher.Errors:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("File watcher error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = watcher.Add(dir)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error adding directory to watcher: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add subdirectories
|
|
||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if info.IsDir() && path != dir {
|
|
||||||
return watcher.Add(path)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error adding subdirectories to watcher: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block forever
|
|
||||||
select {}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleFileChange(path string, isNew bool) {
|
|
||||||
if filepath.Ext(path) == ".md" {
|
|
||||||
creationTimesM.Lock()
|
|
||||||
defer creationTimesM.Unlock()
|
|
||||||
if isNew {
|
|
||||||
creationTimes[path] = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := filepath.Dir(path)
|
|
||||||
blogName := filepath.Base(dir)
|
|
||||||
updateBlogEntries(blogName, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateBlogEntries(blogName, path string) {
|
|
||||||
for i, blog := range blogs {
|
|
||||||
if blog.Name == blogName {
|
|
||||||
content, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error reading file: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
number, err := strconv.Atoi(strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error extracting number from file: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := BlogEntry{
|
|
||||||
Content: string(content),
|
|
||||||
Date: creationTimes[path],
|
|
||||||
Number: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
updated := false
|
|
||||||
for j, e := range blogs[i].Entries {
|
|
||||||
if e.Number == number {
|
|
||||||
blogs[i].Entries[j] = entry
|
|
||||||
updated = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !updated {
|
|
||||||
blogs[i].Entries = append(blogs[i].Entries, entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(blogs[i].Entries, func(a, b int) bool {
|
|
||||||
return blogs[i].Entries[a].Number > blogs[i].Entries[b].Number
|
|
||||||
})
|
|
||||||
|
|
||||||
log.Printf("Updated blog %s with entry %d", blogName, number)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If blog not found, create new one
|
|
||||||
content, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error reading file: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
number, err := strconv.Atoi(strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error extracting number from file: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := BlogEntry{
|
|
||||||
Content: string(content),
|
|
||||||
Date: creationTimes[path],
|
|
||||||
Number: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
newBlog := Blog{
|
|
||||||
Name: blogName,
|
|
||||||
Entries: []BlogEntry{entry},
|
|
||||||
}
|
|
||||||
|
|
||||||
blogs = append(blogs, newBlog)
|
|
||||||
log.Printf("Created new blog %s with entry %d", blogName, number)
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderBlogEntry(w http.ResponseWriter, blogName string, entryNumber int) {
|
|
||||||
var blog Blog
|
|
||||||
for _, b := range blogs {
|
|
||||||
if b.Name == blogName {
|
|
||||||
blog = b
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var entry BlogEntry
|
|
||||||
var prevLink, nextLink string
|
|
||||||
for i, e := range blog.Entries {
|
|
||||||
if e.Number == entryNumber {
|
|
||||||
entry = e
|
|
||||||
if i > 0 {
|
|
||||||
prevLink = fmt.Sprintf("/%s/%d", blog.Name, blog.Entries[i-1].Number)
|
|
||||||
}
|
|
||||||
if i < len(blog.Entries)-1 {
|
|
||||||
nextLink = fmt.Sprintf("/%s/%d", blog.Name, blog.Entries[i+1].Number)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
htmlContent := blackfriday.Run([]byte(entry.Content))
|
|
||||||
content := fmt.Sprintf("<div class=\"blog-entry\"><div>%s</div></div>", htmlContent)
|
|
||||||
|
|
||||||
pageData := PageData{
|
|
||||||
Title: "",
|
|
||||||
Description: "",
|
|
||||||
BlogLinks: getBlogLinks(),
|
|
||||||
Content: template.HTML(content),
|
|
||||||
PrevLink: prevLink,
|
|
||||||
NextLink: nextLink,
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTemplate(w, pageData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBlogLinks() []string {
|
|
||||||
var links []string
|
|
||||||
for _, blog := range blogs {
|
|
||||||
links = append(links, blog.Name)
|
|
||||||
}
|
|
||||||
return links
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderTemplate(w http.ResponseWriter, pageData PageData) {
|
|
||||||
tmpl, err := template.ParseFiles(filepath.Join(templateDir, "news.html"))
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
log.Printf("Error parsing template: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tmpl.Execute(w, pageData)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
log.Printf("Error executing template: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderIndex(w http.ResponseWriter) {
|
|
||||||
tmpl, err := template.ParseFiles(filepath.Join(templateDir, "index.html"))
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
log.Printf("Error parsing template: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tmpl.Execute(w, nil)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
log.Printf("Error executing template: %v", err)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderAllBlogs(w http.ResponseWriter, r *http.Request, page int) {
|
|
||||||
var allEntries []BlogEntry
|
|
||||||
|
|
||||||
for _, blog := range blogs {
|
|
||||||
allEntries = append(allEntries, blog.Entries...)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(allEntries, func(i, j int) bool {
|
|
||||||
return allEntries[i].Date.After(allEntries[j].Date)
|
|
||||||
})
|
|
||||||
|
|
||||||
totalEntries := len(allEntries)
|
|
||||||
startIndex := (page - 1) * pageSize
|
|
||||||
endIndex := startIndex + pageSize
|
|
||||||
if startIndex >= totalEntries {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if endIndex > totalEntries {
|
|
||||||
endIndex = totalEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
pagedEntries := allEntries[startIndex:endIndex]
|
|
||||||
|
|
||||||
var content strings.Builder
|
|
||||||
for _, entry := range pagedEntries {
|
|
||||||
htmlContent := blackfriday.Run([]byte(entry.Content))
|
|
||||||
content.WriteString(fmt.Sprintf("<div class=\"blog-entry\"><div>%s</div></div>", htmlContent))
|
|
||||||
}
|
|
||||||
|
|
||||||
var prevLink, nextLink string
|
|
||||||
if startIndex > 0 {
|
|
||||||
prevLink = fmt.Sprintf("/all?page=%d", page-1)
|
|
||||||
}
|
|
||||||
if endIndex < totalEntries {
|
|
||||||
nextLink = fmt.Sprintf("/all?page=%d", page+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTemplate(w, PageData{
|
|
||||||
Title: "All Blogs",
|
|
||||||
Description: "Combined blog entries from all blogs.",
|
|
||||||
BlogLinks: getBlogLinks(),
|
|
||||||
Content: template.HTML(content.String()),
|
|
||||||
PrevLink: prevLink,
|
|
||||||
NextLink: nextLink,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFileModTime(path string) (time.Time, error) {
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
return info.ModTime(), nil
|
|
||||||
}
|
}
|
||||||
|
|
67
rss.go
|
@ -1,67 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/feeds"
|
|
||||||
)
|
|
||||||
|
|
||||||
func generateRSSFeed(w http.ResponseWriter, blogs []Blog, siteURL string) {
|
|
||||||
feed := &feeds.Feed{
|
|
||||||
Title: "My Blog",
|
|
||||||
Link: &feeds.Link{Href: siteURL},
|
|
||||||
Description: "A blog about various topics.",
|
|
||||||
Author: &feeds.Author{Name: "Your Name", Email: "your-email@example.com"},
|
|
||||||
Created: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, blog := range blogs {
|
|
||||||
for _, entry := range blog.Entries {
|
|
||||||
feed.Items = append(feed.Items, &feeds.Item{
|
|
||||||
Title: fmt.Sprintf("Entry %d", entry.Number), // Use entry number as title
|
|
||||||
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/%d", siteURL, blog.Name, entry.Number)},
|
|
||||||
Description: entry.Content,
|
|
||||||
Created: entry.Date,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rss, err := feed.ToRss()
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Error generating RSS feed", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/rss+xml")
|
|
||||||
w.Write([]byte(rss))
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateBlogRSSFeed(w http.ResponseWriter, blog Blog, siteURL string) {
|
|
||||||
feed := &feeds.Feed{
|
|
||||||
Title: blog.Name,
|
|
||||||
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", siteURL, blog.Name)},
|
|
||||||
Description: blog.Name,
|
|
||||||
Author: &feeds.Author{Name: "Your Name", Email: "your-email@example.com"},
|
|
||||||
Created: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range blog.Entries {
|
|
||||||
feed.Items = append(feed.Items, &feeds.Item{
|
|
||||||
Title: fmt.Sprintf("Entry %d", entry.Number), // Use entry number as title
|
|
||||||
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/%d", siteURL, blog.Name, entry.Number)},
|
|
||||||
Description: entry.Content,
|
|
||||||
Created: entry.Date,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
rss, err := feed.ToRss()
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Error generating RSS feed", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/rss+xml")
|
|
||||||
w.Write([]byte(rss))
|
|
||||||
}
|
|
3
run.sh
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
go run discord.go rss.go telegram.go main.go
|
|
26
telegram.go
|
@ -1,26 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
func startTelegramBot() {
|
|
||||||
u := tgbotapi.NewUpdate(0)
|
|
||||||
u.Timeout = 60
|
|
||||||
|
|
||||||
updates := bot.GetUpdatesChan(u)
|
|
||||||
|
|
||||||
for update := range updates {
|
|
||||||
if update.Message == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text)
|
|
||||||
_, err := bot.Send(msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error sending message: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<!--
|
|
||||||
Stellar by HTML5 UP
|
|
||||||
html5up.net | @ajlkn
|
|
||||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
|
||||||
-->
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
|
||||||
<title>{{.Title}}</title>
|
|
||||||
<meta content="🌐 {{.Title}}" property="og:title" />
|
|
||||||
<meta content="Privacy respecting user friendly web browser." property="og:description" />
|
|
||||||
<meta content="https://spitfirebrowser.com/" property="og:url" />
|
|
||||||
<meta content="https://spitfirebrowser.com/favicon.png" property="og:image" />
|
|
||||||
<meta content="#f1f1f1" data-react-helmet="true" name="theme-color" />
|
|
||||||
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
|
||||||
<link rel="stylesheet" href="../assets/css/main.css" />
|
|
||||||
<link rel="stylesheet" href="../assets/css/stars.css" />
|
|
||||||
<noscript><link rel="stylesheet" href="../assets/css/noscript.css" /></noscript>
|
|
||||||
<style>
|
|
||||||
/* Ensure the wrapper content remains above the background */
|
|
||||||
#wrapper {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="is-preload">
|
|
||||||
|
|
||||||
<!-- Star Background Divs -->
|
|
||||||
<div id="star-background">
|
|
||||||
<div id="stars"></div>
|
|
||||||
<div id="stars2"></div>
|
|
||||||
<div id="stars3"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Wrapper -->
|
|
||||||
<div id="wrapper">
|
|
||||||
|
|
||||||
<!-- Header -->
|
|
||||||
<header id="header">
|
|
||||||
<h1>{{.Title}}</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- Main -->
|
|
||||||
<div id="main">
|
|
||||||
<!-- Stable Release Section -->
|
|
||||||
<section class="download-section">
|
|
||||||
<div class="center-text">
|
|
||||||
<div class="blog-entries">
|
|
||||||
{{.Content}}
|
|
||||||
</div>
|
|
||||||
<div class="pagination">
|
|
||||||
{{if .PrevLink}}
|
|
||||||
<a href="{{.PrevLink}}" class="prev">Previous</a>
|
|
||||||
{{end}}
|
|
||||||
{{if .NextLink}}
|
|
||||||
<a href="{{.NextLink}}" class="next">Next</a>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer id="footer">
|
|
||||||
<section>
|
|
||||||
<h2>Support me</h2>
|
|
||||||
<ul class="icons">
|
|
||||||
<li>
|
|
||||||
<a href="#" class="icon alt">
|
|
||||||
<img src="static/images/icons/regular/heart.svg" alt="LibrePay">
|
|
||||||
<span class="label">LibrePay</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://weforgecode.xyz/Spitfire/" class="icon alt">
|
|
||||||
<img src="static/images/icons/brands/git-alt.svg" alt="Forgejo">
|
|
||||||
<span class="label">Forgejo</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" class="icon alt">
|
|
||||||
<img src="static/images/icons/brands/youtube.svg" alt="YouTube">
|
|
||||||
<span class="label">YouTube</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<p class="copyright">© Spitfire Browser. Design based on <a href="https://html5up.net">HTML5 UP</a>.</p>
|
|
||||||
</section>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scripts -->
|
|
||||||
<script src="assets/js/jquery.min.js" defer></script>
|
|
||||||
<script src="assets/js/jquery.scrollex.min.js" defer></script>
|
|
||||||
<script src="assets/js/jquery.scrolly.min.js" defer></script>
|
|
||||||
<script src="assets/js/browser.min.js" defer></script>
|
|
||||||
<script src="assets/js/breakpoints.min.js" defer></script>
|
|
||||||
<script src="assets/js/util.js" defer></script>
|
|
||||||
<script src="assets/js/main.js" defer></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|