Compare commits

...

3 commits

Author SHA1 Message Date
partisan
0165c7e6f7 Init 2024-08-14 12:55:34 +02:00
partisan
63c04a9b44 wip 2024-08-14 12:43:07 +02:00
partisan
22018120d7 fix typo 2024-08-14 12:21:28 +02:00
78 changed files with 789 additions and 47 deletions

View file

@ -4,10 +4,13 @@ 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 working downloads - [ ] Add search-engine test
[ ] Add blog/updates
- [ ] Add working downloads
- [ ] Add blog/updates
### Based on: ### Based on:

5
data/news/1.md Normal file
View file

@ -0,0 +1,5 @@
# 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.

3
data/news/2.md Normal file
View file

@ -0,0 +1,3 @@
# 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.

3
data/news/3.md Normal file
View file

@ -0,0 +1,3 @@
# 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.

3
data/news/4.md Normal file
View file

@ -0,0 +1,3 @@
# 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 Normal file
View file

@ -0,0 +1,34 @@
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)
}
}

13
go.mod Normal file
View file

@ -0,0 +1,13 @@
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 Normal file
View file

@ -0,0 +1,14 @@
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=

476
main.go
View file

@ -1,19 +1,479 @@
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"
) )
func main() { const (
// Define the directory where your HTML and CSS files are located dataDir = "./data"
http.Handle("/", http.FileServer(http.Dir("."))) 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
)
// Start the web server on specfied port type Blog struct {
port := 10369 Name string
fmt.Printf("Server is running on http://localhost:%d\n", port) Entries []BlogEntry
err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil) }
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() {
flag.Parse()
botToken := os.Getenv("TELEGRAM_BOT_TOKEN")
if botToken == "" {
botToken = botTokenEnv
}
var err error
bot, err = tgbotapi.NewBotAPI(botToken)
if err != nil { if err != nil {
fmt.Println("Error:", err) log.Printf("Warning: Error creating Telegram bot: %v", 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 Normal file
View file

@ -0,0 +1,67 @@
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 Executable file
View file

@ -0,0 +1,3 @@
go run discord.go rss.go telegram.go main.go

View file

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

Before

Width:  |  Height:  |  Size: 780 B

After

Width:  |  Height:  |  Size: 780 B

View file

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 915 B

View file

Before

Width:  |  Height:  |  Size: 1,013 B

After

Width:  |  Height:  |  Size: 1,013 B

View file

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 414 B

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

26
telegram.go Normal file
View file

@ -0,0 +1,26 @@
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)
}
}
}

View file

@ -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="images/icons/regular/heart.svg" alt="LibrePay"> <img src="static/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="images/icons/brands/git-alt.svg" alt="Forgejo"> <img src="static/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="images/icons/brands/youtube.svg" alt="YouTube"> <img src="static/images/icons/brands/youtube.svg" alt="YouTube">
<span class="label">YouTube</span> <span class="label">YouTube</span>
</a> </a>
</li> </li>

View file

@ -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="images/icons/brands/windows.svg" alt="Windows"> <img src="static/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="images/icons/brands/apple.svg" alt="MacOS"> <img src="static/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="images/icons/brands/linux.svg" alt="Linux"> <img src="static/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="images/icons/brands/linux.svg" alt="Linux (Appimage)"> <img src="static/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="images/icons/brands/chrome.svg" alt="ChromeOS"> <img src="static/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="images/icons/brands/windows.svg" alt="Windows Nightly"> <img src="static/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="images/icons/brands/linux.svg" alt="Linux (Flatpak) Nightly"> <img src="static/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="images/icons/regular/heart.svg" alt="LibrePay"> <img src="static/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="images/icons/brands/git-alt.svg" alt="Forgejo"> <img src="static/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="images/icons/brands/youtube.svg" alt="YouTube"> <img src="static/images/icons/brands/youtube.svg" alt="YouTube">
<span class="label">YouTube</span> <span class="label">YouTube</span>
</a> </a>
</li> </li>

View file

@ -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="assets/css/main.css" /> <link rel="stylesheet" href="static/css/main.css" />
<link rel="stylesheet" href="assets/css/stars.css" /> <link rel="stylesheet" href="static/css/stars.css" />
<link rel="stylesheet" href="assets/css/fancy-gallery.css" /> <link rel="stylesheet" href="static/css/fancy-gallery.css" />
<noscript><link rel="stylesheet" href="assets/css/noscript.css" /></noscript> <noscript><link rel="stylesheet" href="static/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="images/logo.svg" alt="Spitfire Logo" /></span> <span class="logo"><img src="static/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="images/screenshots/1.png" alt="Screenshot 1" /></div> <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 2" /></div> <div class="gallery-item"><img src="static/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="images/icons/solid/lock.svg" alt="Lock"> <img src="static/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="images/icons/regular/eye-slash.svg" alt="Eye-slash"> <img src="static/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="images/icons/solid/arrows-rotate.svg" alt="arrows-rotate"> <img src="static/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="images/icons/brands/firefox-browser.svg" alt="Firefox"> <img src="static/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="images/icons/solid/code.svg" alt="code"> <img src="static/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="images/icons/regular/heart.svg" alt="LibrePay"> <img src="static/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="images/icons/brands/git-alt.svg" alt="Forgejo"> <img src="static/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="images/icons/brands/youtube.svg" alt="YouTube"> <img src="static/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="assets/js/jquery.min.js" defer></script> <script src="static/js/jquery.min.js" defer></script>
<script src="assets/js/jquery.scrollex.min.js" defer></script> <script src="static/js/jquery.scrollex.min.js" defer></script>
<script src="assets/js/jquery.scrolly.min.js" defer></script> <script src="static/js/jquery.scrolly.min.js" defer></script>
<script src="assets/js/browser.min.js" defer></script> <script src="static/js/browser.min.js" defer></script>
<script src="assets/js/breakpoints.min.js" defer></script> <script src="static/js/breakpoints.min.js" defer></script>
<script src="assets/js/util.js" defer></script> <script src="static/js/util.js" defer></script>
<script src="assets/js/main.js" defer></script> <script src="static/js/main.js" defer></script>
</body> </body>
</html> </html>

108
templates/news.html Normal file
View file

@ -0,0 +1,108 @@
<!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">&copy; 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>