summary refs log tree commit diff stats
diff options
context:
space:
mode:
authormounderfod <mounderfod@gmail.com>2023-07-19 14:40:29 +0200
committermounderfod <mounderfod@gmail.com>2023-07-19 14:40:29 +0200
commit328f2998b27ef189dac893d081d3626854fc20d4 (patch)
tree921f13ceb69f5bdadc9645326b2b924167be08f9
downloadgopherhole-328f2998b27ef189dac893d081d3626854fc20d4.tar.gz
Initial commit
-rw-r--r--.gitignore2
-rw-r--r--.idea/.gitignore8
-rw-r--r--.idea/encodings.xml6
-rw-r--r--.idea/gophersite.iml21
-rw-r--r--.idea/inspectionProfiles/Project_Default.xml12
-rw-r--r--.idea/inspectionProfiles/profiles_settings.xml6
-rw-r--r--.idea/misc.xml4
-rw-r--r--.idea/modules.xml8
-rw-r--r--__pycache__/app.cpython-311.pycbin0 -> 952 bytes
-rw-r--r--__pycache__/news.cpython-311.pycbin0 -> 2642 bytes
-rw-r--r--__pycache__/weather.cpython-311.pycbin0 -> 7354 bytes
-rw-r--r--app.py47
-rw-r--r--news.py34
-rw-r--r--personal/gophermap13
-rw-r--r--personal/phlog/23-06-23-welcome.txt15
-rw-r--r--personal/phlog/23-07-11-fediverse.txt94
-rw-r--r--personal/phlog/23-07-18-emacs.txt60
-rw-r--r--personal/phlog/gophermap7
-rw-r--r--requirements.txt4
-rw-r--r--static/ascii/cat.txt20
-rw-r--r--weather.py112
21 files changed, 473 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9bec4dc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.env
+venv/
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..c3a317a
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/static/ascii/cat.txt" charset="ISO-8859-1" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/gophersite.iml b/.idea/gophersite.iml
new file mode 100644
index 0000000..e2b9085
--- /dev/null
+++ b/.idea/gophersite.iml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="Flask">
+    <option name="enabled" value="true" />
+  </component>
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/venv" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+  <component name="TemplatesService">
+    <option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
+    <option name="TEMPLATE_FOLDERS">
+      <list>
+        <option value="$MODULE_DIR$/../gophersite\templates" />
+      </list>
+    </option>
+  </component>
+</module>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..d4a00d6
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,12 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
+      <option name="ignoredErrors">
+        <list>
+          <option value="E302" />
+        </list>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..4cfc957
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (gophersite)" project-jdk-type="Python SDK" />
+</project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..1b01f16
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/gophersite.iml" filepath="$PROJECT_DIR$/.idea/gophersite.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/__pycache__/app.cpython-311.pyc b/__pycache__/app.cpython-311.pyc
new file mode 100644
index 0000000..dd2a1a1
--- /dev/null
+++ b/__pycache__/app.cpython-311.pyc
Binary files differdiff --git a/__pycache__/news.cpython-311.pyc b/__pycache__/news.cpython-311.pyc
new file mode 100644
index 0000000..da743bd
--- /dev/null
+++ b/__pycache__/news.cpython-311.pyc
Binary files differdiff --git a/__pycache__/weather.cpython-311.pyc b/__pycache__/weather.cpython-311.pyc
new file mode 100644
index 0000000..7fa807e
--- /dev/null
+++ b/__pycache__/weather.cpython-311.pyc
Binary files differdiff --git a/app.py b/app.py
new file mode 100644
index 0000000..702045c
--- /dev/null
+++ b/app.py
@@ -0,0 +1,47 @@
+import os
+from os import listdir
+from os.path import isfile, join
+from dotenv import load_dotenv
+from pituophis import Item, serve
+from pyfiglet import Figlet
+import news
+import weather
+
+load_dotenv()
+figlet = Figlet(font="big")
+
+
+def handle(request):
+    if request.path == "" or request.path == "/":
+        menu = []
+        with open("static/ascii/cat.txt", "r") as f:
+            menu += [Item(text=i) for i in f.readlines()]
+
+        menu.append(Item(itype="1", text="......NEWS", path="/news", host=os.getenv("HOSTNAME")))
+        menu.append(
+            Item(itype="7", text="......WEATHER (type in city name)", path="/weather", host=os.getenv("HOSTNAME")))
+        menu.append(Item(itype="1", text="......OWNER'S SITE", path="/personal", host=os.getenv("HOSTNAME")))
+        return menu
+    elif request.path.startswith("/newstxt"):
+        return news.get_newstxt(request.path.split("?article=")[1])
+    elif request.path.startswith("/weathertxt"):
+        return weather.get_weather(request.path)
+    elif request.path == "/personal":
+        with open("personal/gophermap", "r") as f:
+            return [i for i in f.readlines()]
+    elif request.path == "/news" or request.path == "/weather":
+        menu = []
+        text = figlet.renderText(request.path[1:]).split("\n")
+        menu += [Item(text=i) for i in text]
+        match request.path:
+            case "/news":
+                menu.append(Item(text="=== Provided by The Guardian ==="))
+                menu += news.get_news()
+            case "/weather":
+                menu += weather.get_cities(request.query)
+        return menu
+    else:
+        return [Item(itype="3", text="Page not found")]
+
+
+serve(os.getenv("HOSTNAME"), port=70, handler=handle)
diff --git a/news.py b/news.py
new file mode 100644
index 0000000..6894d6d
--- /dev/null
+++ b/news.py
@@ -0,0 +1,34 @@
+import os
+import urllib.parse
+import html2text
+import requests
+from dotenv import load_dotenv
+from pituophis import Item
+
+load_dotenv()
+
+def get_news():
+    results = []
+    data = requests.get(f"https://content.guardianapis.com/search?api-key={os.getenv('GUARDIAN_API')}&page-size=50").json()
+    results += [Item(
+        itype="0",
+        text=i['webTitle'],
+        path=f"/newstxt?article={urllib.parse.quote(i['id'], safe='')}",
+        host=os.getenv("HOSTNAME")
+    ) for i in data['response']['results']]
+    return results
+
+
+def get_newstxt(article):
+    path = urllib.parse.unquote(article)
+    data = requests.get(f"https://content.guardianapis.com/{path}?api-key={os.getenv('GUARDIAN_API')}&show-fields=body").json()
+    h = html2text.HTML2Text()
+    h.use_automatic_links = True
+    h.images_to_alt = True
+    h.ignore_tables = True
+    h.ignore_links = True
+    h.emphasis_mark = ""
+    h.strong_mark = ""
+    text = f"{data['response']['content']['webTitle']}\n" + ("=" * 60) + "\n"
+    text += h.handle(data['response']['content']['fields']['body'])
+    return text
\ No newline at end of file
diff --git a/personal/gophermap b/personal/gophermap
new file mode 100644
index 0000000..71ffe3e
--- /dev/null
+++ b/personal/gophermap
@@ -0,0 +1,13 @@
+i                  \   /\    ----------------		null.host	1
+i                  )  ( ')  <|  mounderfod  |		null.host	1
+i                 (  / )     ----------------		null.host	1
+i                 \(__)|		null.host	1
+i		null.host	1
+iWelcome to my gopher space! My name is Noah, but I go by mounderfod.		null.host	1
+iI am interested in music, programming and video games.		null.host	1
+i		null.host	1
+iLanguages I speak: French, English		null.host	1
+iPronouns: he/him		null.host	1
+i		null.host	1
+hMy WWW site	URL:https://mounderfod.online	gopher.mounderfod.online	70
+1Phlog (mirror of my blog)	/personal/phlog	gopher.mounderfod.online	70
diff --git a/personal/phlog/23-06-23-welcome.txt b/personal/phlog/23-06-23-welcome.txt
new file mode 100644
index 0000000..dd74824
--- /dev/null
+++ b/personal/phlog/23-06-23-welcome.txt
@@ -0,0 +1,15 @@
+Welcome to my blog!
+===================
+23 June 2023 | https://www.mounderfod.online/2023/06/23/welcome-to-my-blog.html
+
+Hello, I have decided to set up a blog on my website :) 
+Basically, I will use this page to create more long-form posts to express various ideas and other things that 
+I've found cool recently. 
+
+How does the website work? 
+--------------------------
+The frontend was all written by me in raw HTML/CSS. 
+The little time marquee at the top of the page was my own idea, 
+but I "borrowed" the time formatting code from Stack Overflow. 
+In terms of backend, the website uses Jekyll and is being hosted on GitHub using Vercel to deploy it. 
+If anything is not working or you just want to contact me, contact me on Discord @mounderfod.
diff --git a/personal/phlog/23-07-11-fediverse.txt b/personal/phlog/23-07-11-fediverse.txt
new file mode 100644
index 0000000..ef30e1b
--- /dev/null
+++ b/personal/phlog/23-07-11-fediverse.txt
@@ -0,0 +1,94 @@
+Enter The Fediverse
+===================
+11 July 2023 | https://www.mounderfod.online/2023/07/11/enter-the-fediverse.html
+
+If you havent heard, Reddit is in a bit of a pickle. In short, they have changed their API pricing
+in such a way as to effectively make it impossible for 3rd party apps to continue, presumably in
+order to improve that sweet ad revenue (a move probably inspired by Twitter's). In any case, the lack 
+of Reddit for a few days (and my general dissatisfaction with the platform at that time) led me to 
+explore alternatives, which led me to Lemmy, a finding which would cause me to dive much deeper into a much 
+wider thing - the Fediverse. This article will explore this process and how Ive found it so far.
+
+Lemmy
+----- 
+As I said, the first thing that I'd found for this was Lemmy. Lemmy is, according to its own website:
+
+"a selfhosted social link aggregation and discussion platform. It is completely free and open, and not 
+controlled by any company. This means that there is no advertising, tracking, or secret algorithms. Content is 
+organized into communities, so it is easy to subscribe to topics that you are interested in, and ignore others. 
+Voting is used to bring the most interesting items to the top."
+
+Sounds cool, and most importantly, very similar to Reddit UX-wise, so I went and made an account.
+
+I should probably explain something out of the gate - there isnt one Lemmy website. Thats the whole point of 
+the Fediverse. Instead, there are many instances of Lemmy, all of which are federated, that is to say, they are 
+all interconnected. From my instance, I can make a post on another instance, which a user from yet another 
+instance can comment on. This way, there is no central authority for the whole site. Its like crypto, but 
+without the enormous range of scams and profiteering.
+
+Anyway, the instance that I decided to start with was lemmy.blahaj.zone. I was told that the instance you start 
+with doesnt really matter, so I chose this one on somewhat silly grounds - I own a Blahaj myself.
+
+It worked pretty well to begin with, as from my perspective it seemed to be working just like Reddit; I could 
+make posts, join communities, and so on. However, I quickly noticed that choosing an instance wasnt going to be 
+as simple for me as I had anticipated, due to my own pickiness and the particulars of federation.
+
+How federation works (a crude explanation)
+------------------------------------------
+Obviously, it would be wildly inefficient if every server shared everything that happened on it with every 
+server. As a result, federation on ActivityPub (the protocol that Lemmy, Mastodon, and more - including the 
+dreaded Meta Threads - use as their base) works like this:
+
+1. a user on server A requests a post/user/community on server B by using the search function 
+2. server B provides what they requested and creates a federated link between it and server A
+3. any future activity on the post/user/community is sent to server A for anyone to see
+
+This is probably an oversimplification, and it doesnt work 100% of the time (particularly when federating 
+between servers running different software), but from my observations this is basically how it works.
+
+This is also where my issue with the instance Id chosen came up. There was nothing inherently wrong with the 
+instance or its admins, and in fact it was a lovely instance, but several of the communities that I was 
+interested in (such as the Modded Minecraft or RetroWeb ones) had not yet federated properly. This is no fault 
+of the instances, and once I had searched for them myself, any new activity would be visible to me. But I am an 
+impatient moron, and I wanted to see what the existing activity was, and besides I began to notice that not all 
+comments on posts were being federated to me (such that going to the home instance of the post showed more 
+comments than I could see or respond to on my own instance), and this bothered me for some reason, so I decided 
+that I would begin to look for another instance.
+
+Side note: This issue is probably fixed now as the instance has grown some since the time of this experience.
+
+Conveniently, it became apparent to me on that same day that the SDF network, a lovely network of Internet 
+services, including a public Unix shell, had recently set up a Lemmy server. I had been a member of SDF for a 
+couple of weeks, and had been using their IRC and bulletin board during that time, so I figured I would set up 
+an account there - and besides, I checked in advance, and the federation seemed to be more to my liking - so 
+now Ive been using lemmy.sdf.org as my home instance and its been great - my username is 
+@mounderfod@lemmy.sdf.org if youre curious.
+
+Mastodon 
+-------- 
+Of course, if youre familiar with the Fediverse (or have read the article up to this point), 
+you know that Lemmy is not the only Fediverse service that exists. Next for me was a replacement for Twitter, 
+which Id ditched on the day it was bought by Elon Musk (a fact which turned out to be excellent foresight on my 
+part, but realistically I was going to delete my account anyway, so it wasnt exactly a stroke of genius).
+
+This one was a little easier, since I discovered that SDF also had a Mastodon instance. I have to say that in 
+recent times Ive found myself using Mastodon a lot more than Lemmy; theres more users so theres more content 
+for me to access, and the lack of algorithm is really refreshing because it allows me to build my feed with 
+only the content (and people) that I want to see.
+
+If youd like to follow me on Mastodon my account is @mounderfod@mastodon.sdf.org.
+
+Other services and conclusion
+-----------------------------
+By this point I was fully immersed in the Fediverse, and quickly set up Pixelfed to replace Instagram and 
+Funkwhale to store my personal music collection. I also intend to set up my website with IndieWeb, which is 
+basically federation for blogs.
+
+At the start of this article, I explained that this all started because of Reddits API changes. But the truth 
+is, I was starting to become disillusioned with the mainstream social media networks long before this, with the 
+constant algorithms, ragebait and promotion of far-right content putting a drain on my own energy. I was 
+perhaps longing for freedom to control what I put my attention on. If this sounds like you, then I would 
+strongly recommend that you at least try the Fediverse networks - in any case, its much easier to delete your 
+account with them than with e.g. Facebook if you dont like it!
+
+I hope this article was of at least some interest to you, and thank you for making it this far :D
diff --git a/personal/phlog/23-07-18-emacs.txt b/personal/phlog/23-07-18-emacs.txt
new file mode 100644
index 0000000..f59db3f
--- /dev/null
+++ b/personal/phlog/23-07-18-emacs.txt
@@ -0,0 +1,60 @@
+Using Emacs
+===========
+18 July 2023 | https://www.mounderfod.online/2023/07/18/using-emacs.html
+
+This post is being written in Emacs :) 
+
+What? 
+-----
+Emacs is, according to its own website: 
+
+"An extensible, customizable, free/libre text editor and more." 
+
+Basically, its one of the oldest text editors to exist, is (technically) entirely keyboard-based, and manages to combine simplicity with 
+power. In short, its great and Im going to talk about it now. 
+
+Why? 
+----
+Why am I using Emacs? Well, theres a few reasons: 
+
+- Id heard of it before and it sounded cool 
+- Its complex enough that it would present an interesting learning curve, but not so difficult as to discourage me 
+- Its useful for editing posts and HTML like this 
+- It ships with Tetris built in (need I say more?)
+
+How? 
+----
+I went to the website and downloaded it. My laptop currently uses Windows, and Emacs is made by GNU so as 
+expected I was berated for my choice of OS: 
+
+"To improve the use of proprietary systems is a misguided goal. Our aim, rather, is to eliminate them." 
+
+But I wasnt going to concern myself with GNUs plans for world domination; thats a problem for another day. The install was fairly simple, like any other
+application, and upon running the program I am greeted with a pleasant menu screen. 
+
+Now it was time for me to learn how to use Emacs. Emacs is primarily keyboard-based, as it was developed at a time where not all computers had GUIs at all, 
+let alone mice to interact with them. As such, and also due to its age, it has its own set of keybinding patterns which are overall very different to that 
+of most applications. For example, saving a file in MS Word is Ctrl-S, while in Emacs it is C-x C-s, which means Ctrl-x followed by Ctrl-S. Youll notice that in 
+this example, two keybindings need to be pressed to perform one action. This is common in Emacs, as there are lots of commands and not many keys, 
+and there are even some commands that dont have keybindings and must be invoked by pressing M-x (M meaning Alt) and then typing the command name out. 
+
+This was all a bit complex for me to understand at first, but I quickly got the hang of it (as I had done with the more standard keybinding patterns that existed 
+elsewhere in the computing world). 
+
+Customising Emacs 
+-----------------
+Now that I had gotten the grips of Emacs' basic usage, I needed to tailor it to my own needs. My plan was to use Emacs for editing Markdown posts (such as this one) 
+or HTML files, and my website is hosted on GitHub, so I needed something to cover both bases. 
+
+For the latter, there was already Emacs VersionControl, but this was a generic version control tool 
+and wasnt tailored to the specifics of Git. Therefore, I did some googling and came across Magit. A few more googles educated me in how to add the package 
+repository it was in and how to then install the package (M-x package-install RET magit RET), and I was quickly able to clone, commit, and push to the website 
+repository. Perfect! Now I needed to improve my Markdown editing experience. .md files are text, and so I could edit them as normal in Emacs, but then I wouldnt 
+be able to enjoy things such as syntax highlighting and easy access to various formatting options without typing them out manually. Again, a quick google found 
+markdown-mode, and within moments it was installed. The package adds a major mode to Emacs - Emacs is mode-based, meaning that there are modes of editing which 
+result in different functionality of the editor for different purposes - in this case, the markdown mode (enabled with M-x markdown-mode) provides syntax 
+highlighting and commands to automatically paste in the syntax for links, etc. 
+
+And that was it! 
+I had installed, learned to use, and configured Emacs and could now use it to edit blog posts for this very website (or Gopher phlog, if youre reading it on that 
+mirror). Next I shall get it set up for developing my Python/Java projects - Ill keep you posted!
diff --git a/personal/phlog/gophermap b/personal/phlog/gophermap
new file mode 100644
index 0000000..71ea7c9
--- /dev/null
+++ b/personal/phlog/gophermap
@@ -0,0 +1,7 @@
+1Back to homepage	/	gopher.mounderfod.online	70
+i		null.host	1
+imounderfod's Phlog (Gopher blog)		null.host	1
+i		null.host	1
+018 July 2023 - Using Emacs	/personal/phlog/23-07-18-emacs.txt	gopher.mounderfod.online	70
+011 July 2023 - Enter the Fediverse	/personal/phlog/23-07-11-fediverse.txt	gopher.mounderfod.online	70
+023 June 2023 - Welcome to my blog!	/personal/phlog/23-06-23-welcome.txt	gopher.mounderfod.online    70
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..3f7a82b
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+python-dotenv~=1.0.0
+Pituophis~=1.1
+pyfiglet~=0.7.6
+requests~=2.31.0
\ No newline at end of file
diff --git a/static/ascii/cat.txt b/static/ascii/cat.txt
new file mode 100644
index 0000000..41fb038
--- /dev/null
+++ b/static/ascii/cat.txt
@@ -0,0 +1,20 @@
+                                 _            __          _
+                                | |          / _|        | |
+ _ __ ___   ___  _   _ _ __   __| | ___ _ __| |_ ___   __| |
+| '_ ` _ \ / _ \| | | | '_ \ / _` |/ _ \ '__|  _/ _ \ / _` |
+| | | | | | (_) | |_| | | | | (_| |  __/ |  | || (_) | (_| |
+|_| |_| |_|\___/ \__,_|_| |_|\__,_|\___|_|  |_| \___/ \__,_|
+
+                  ==================================
+      \    /\    | Welcome to the MOUNDERFOD gopher |
+       )  ( ')  <  server, ran as part of the OSLI  |
+      (  /  )    | services by "mounderfod"         |
+       \(__)|     ==================================
+
+(ascii art by Joan Stark)
+
++------------------------------------------+
+|                                          |
+|               DIRECTORIES                |
+|                                          |
++------------------------------------------+
diff --git a/weather.py b/weather.py
new file mode 100644
index 0000000..50b947a
--- /dev/null
+++ b/weather.py
@@ -0,0 +1,112 @@
+import urllib.parse
+import os
+import requests
+from pituophis import Item
+from dotenv import load_dotenv
+from prettytable import PrettyTable
+from pyfiglet import Figlet
+
+load_dotenv()
+
+
+def get_code(code):
+    match code:
+        case 0:
+            return "Clear sky"
+        case 1 | 2 | 3:
+            return "Partly cloudy"
+        case 45 | 48:
+            return "Foggy"
+        case 51 | 53 | 55:
+            return "Drizzle"
+        case 56 | 57:
+            return "Freezing drizzle"
+        case 61 | 63 | 65:
+            return "Rain"
+        case 66 | 67:
+            return "Freezing rain"
+        case 71 | 73 | 675:
+            return "Snow"
+        case 77:
+            return "Snow grains"
+        case 80 | 81 | 82:
+            return "Rain showers"
+        case 85 | 86:
+            return "Snow showers"
+        case 95:
+            return "Thunderstorm"
+        case 96 | 99:
+            return "Thunderstorm with hail"
+        case _:
+            return "Unknown"
+
+
+def get_cities(query):
+    data = requests.get(
+        f"https://geocoding-api.open-meteo.com/v1/search?name={query}&count=10&language=en&format=json").json()
+    if "results" in data:
+        return [Item(
+            itype="0",
+            text=f"{i['name']} ({i['admin1'] + ', ' if 'admin1' in i else ''}{i['country']})",
+            path=f"/weathertxt?latlong={str(i['latitude'])}@{str(i['longitude'])}&city={i['name']}",
+            host=os.getenv("HOSTNAME")
+        ) for i in data['results']]
+    else:
+        return [Item(itype="3", text="City could not be found")]
+
+
+def get_weather(city):
+    result = []
+    query = city.split("?latlong=")[1]
+    latitude = query.split("&city=")[0].split("@")[0]
+    longitude = query.split("&city=")[0].split("@")[1]
+    place = query.split("&city=")[1]
+
+    print(f"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&hourly"
+                        f"=temperature_2m,relativehumidity_2m,precipitation_probability,"
+                        f"weathercode,windspeed_10m&daily=weathercode,"
+                        f"temperature_2m_max,temperature_2m_min,sunrise,"
+                        f"sunset&current_weather=true&timezone=auto")
+
+    data = requests.get(f"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&hourly"
+                        f"=temperature_2m,relativehumidity_2m,precipitation_probability,"
+                        f"weathercode,windspeed_10m&daily=weathercode,"
+                        f"temperature_2m_max,temperature_2m_min,sunrise,"
+                        f"sunset&current_weather=true&timezone=auto").json()
+
+    f = Figlet(font="big")
+    result += f.renderText("weather").split("\n")
+    f.setFont(font="small")
+
+    result.append("="*80)
+    result.append(f"{place}".center(80))
+    result.append("="*80)
+
+    result += f.renderText("hourly").split("\n")
+    table_hourly = PrettyTable()
+    table_hourly.field_names = ["Time", "Summary", "Temperature", "Humidity", "Precipitation Chance", "Wind Speed"]
+    for idx, i in enumerate(data['hourly']['time'][:23]):
+        table_hourly.add_row([
+            i[-5:],
+            get_code(data['hourly']['weathercode'][idx]),
+            f"{data['hourly']['temperature_2m'][idx]} C",
+            f"{data['hourly']['relativehumidity_2m'][idx]} %",
+            f"{data['hourly']['precipitation_probability'][idx]} %",
+            f"{data['hourly']['windspeed_10m'][idx]} km/h",
+        ])
+    result.append(table_hourly.get_string())
+    result += ["\n"] * 3
+
+    result += f.renderText("daily").split("\n")
+    table_daily = PrettyTable()
+    table_daily.field_names = ["Date", "Summary", "Temperature (Max/Min)", "Sunrise / Sunset"]
+    for idx, i in enumerate(data['daily']['time']):
+        table_daily.add_row([
+            i,
+            get_code(data['daily']['weathercode'][idx]),
+            f"{data['daily']['temperature_2m_max'][idx]} C / {data['daily']['temperature_2m_min'][idx]} C",
+            f"{data['daily']['sunrise'][idx][-5:]} / {data['daily']['sunset'][idx][-5:]}"
+        ])
+    result.append(table_daily.get_string())
+
+    return result