From 95f23203baef9fbc9e613027ed85dddc243264ea Mon Sep 17 00:00:00 2001 From: Furkan Onder Date: Sat, 24 Apr 2021 01:27:06 +0300 Subject: [PATCH] initial commit --- .gitignore | 137 +++++++++++++++++++++++++++++++++++++++++++++++ LICENSE | 21 ++++++++ README.md | 23 ++++++++ eksi/__init__.py | 0 eksi/__main__.py | 19 +++++++ eksi/color.py | 16 ++++++ eksi/eksi.py | 118 ++++++++++++++++++++++++++++++++++++++++ eksi/version.py | 1 + setup.py | 53 ++++++++++++++++++ 9 files changed, 388 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 eksi/__init__.py create mode 100644 eksi/__main__.py create mode 100644 eksi/color.py create mode 100644 eksi/eksi.py create mode 100644 eksi/version.py create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a415e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,137 @@ +*.pyc +*.db +*.scssc +*.map +media + +# Created by https://www.gitignore.io/api/python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +### Python Patch ### +.venv/ + +### Python.VirtualEnv Stack ### +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +pip-selfcheck.json + + +# End of https://www.gitignore.io/api/python diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d7cbd2e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Furkan Taha ÖNDER + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b503dc2 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# EkşiGündem +Ekşi Gündem, komut satırından Ekşi Sözlük'ün gündem başlıklarını ve entrylerini okumanıza yarayan bir araçtır. + +# Kurulum +Python paket yöneticisi ile kolayca kurabilirsiniz. +```python +pip install eksi +``` +Paket yöneticisi olmadan, bu şekilde de kurabilirsiniz. +```python +python setup.py install +``` + +# Çalıştırma +Terminalinize +```bash +eksi +``` +yazarak kullanabilirsiniz. +Ek olarak, -b parametresini kullanarak okumak istediğiniz başlık sayısını belirtebilirsiniz. +``` +eksi -b 10 +``` diff --git a/eksi/__init__.py b/eksi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/eksi/__main__.py b/eksi/__main__.py new file mode 100644 index 0000000..56ca624 --- /dev/null +++ b/eksi/__main__.py @@ -0,0 +1,19 @@ +import argparse + +from eksi.eksi import Eksi +from eksi.version import __version__ + + +def main(): + parser = argparse.ArgumentParser(description="Komut satırında Ekşi Sözlük!") + parser.add_argument("-v", "--versiyon", action="version", version=__version__) + parser.add_argument( "-b", "--baslik", type=int, choices=range(1, 51), + help="Gösterilecek başlık sayısı", + ) + args = parser.parse_args() + eksi = Eksi() + eksi.main(args.baslik) + + +if __name__ == "__main__": + main() diff --git a/eksi/color.py b/eksi/color.py new file mode 100644 index 0000000..2a61125 --- /dev/null +++ b/eksi/color.py @@ -0,0 +1,16 @@ +from colorama import Fore, init as init_colors + +colors = dict( + red=Fore.RED, + green=Fore.GREEN, + yellow=Fore.YELLOW, + blue=Fore.BLUE, + magenta=Fore.MAGENTA, + cyan=Fore.CYAN, + white=Fore.WHITE, + reset=Fore.RESET, +) + + +def cprint(color, *args, **kwargs): + print(colors[color], *args, Fore.RESET, **kwargs) diff --git a/eksi/eksi.py b/eksi/eksi.py new file mode 100644 index 0000000..9ab6a2f --- /dev/null +++ b/eksi/eksi.py @@ -0,0 +1,118 @@ +import os +import sys +import urllib.request + +from bs4 import BeautifulSoup +from eksi.color import init_colors, cprint + + +class Eksi: + home_page = "https://eksisozluk.com/" + + def __init__(self): + self.page_num = 1 + self.searchable = True + self.topic_url = "" + self.topics = [] + self.topic_limit = 50 + init_colors() + + def chunk(self, l): + for i in range(0, len(l), 3): + yield l[i : i + 3] + + def parser(self, url): + page = urllib.request.urlopen(url) + soup = BeautifulSoup(page, "html.parser") + entries = soup.find_all("ul", {"id": "entry-item-list"}) + soup = BeautifulSoup(str(*entries), "lxml") + lines = [line.strip() for line in soup.get_text().splitlines()] + entry_list = list(filter(lambda line: line != "", lines)) + return self.chunk(entry_list) + + def reader(self, url): + chunk = self.parser(self.home_page + url) + for c in chunk: + cprint("white", c[0]) + cprint("cyan", c[1], c[2]) + cprint("green", "Gündem başlıklarını görüntülemek için: (g)") + cprint("red", "Programdan çıkmak için: (c)") + cprint("cyan", "Sonraki sayfa için: (s)\n Önceki sayfa için: (o)") + + @staticmethod + def clear_screen(): + os.system("cls" if os.name == "nt" else "clear") + + def get_page(self, url, page_num): + self.clear_screen() + try: + if page_num <= 0: + cprint("green", self.topic_title) + self.reader(url + "&p=" + str(1)) + cprint("red", "Şu an ilk sayfadasınız!") + self.page_num = 1 + return + cprint("green", self.topic_title) + self.reader(url + "&p=" + str(self.page_num)) + except urllib.error.HTTPError: + self.reader(url + "&p=" + str(self.page_num - 1)) + cprint("red", "Şu an en son sayfadasınız!") + self.page_num -= 1 + + def prompt(self): + while True: + try: + cmd = input(">>> ") + if cmd == "c": + sys.exit(0) + elif cmd == "g": + self.searchable = True + self.clear_screen() + self.main() + elif cmd == "s" and self.topic_url: + self.page_num += 1 + self.get_page(self.topic_url, self.page_num) + elif cmd == "o" and self.topic_url: + self.page_num -= 1 + self.get_page(self.topic_url, self.page_num) + elif self.searchable and int(cmd) <= self.topic_limit and int(cmd) > 0: + self.searchable = False + self.clear_screen() + self.topic_url = self.topics[int(cmd) - 1].get("href") + self.topic_title = self.topics[int(cmd) - 1].text + cprint("green", self.topic_title) + self.reader(self.topic_url) + else: + cprint("red", "Hata!Geçersiz bir değer girdiniz.") + except (ValueError, IndexError) as error: + cprint("red", "Hata!Geçersiz bir değer girdiniz.") + except (KeyboardInterrupt, EOFError) as error: + break + + def main(self, topic_count=None): + page = urllib.request.urlopen(self.home_page) + soup = BeautifulSoup(page, "html.parser") + agenda = soup.find_all("ul", {"class": "topic-list partial"}) + self.topic_title, self.topic_url = "", "" + self.topics, self.page_num = [], 1 + + for ul in agenda: + for li in ul.find_all("li"): + for topic in li.find_all("a"): + self.topics.append(topic) + + if topic_count: + self.topic_limit = int(topic_count) + else: + self.topic_limit = len(self.topics) + + for topic_id, topic in enumerate(self.topics): + if self.topic_limit > topic_id: + cprint("green", topic_id + 1, "-", end="") + cprint("white", topic.text) + else: + break + + cprint("red", "Programdan çıkmak için: (c)") + cprint("cyan", "Okumak istediğiniz başlık numarası: ") + self.prompt() diff --git a/eksi/version.py b/eksi/version.py new file mode 100644 index 0000000..3dc1f76 --- /dev/null +++ b/eksi/version.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d80c736 --- /dev/null +++ b/setup.py @@ -0,0 +1,53 @@ +from pathlib import Path + +from setuptools import setup + +CURRENT_DIR = Path(__file__).parent +version = {} + +with open("eksi/version.py") as fp: + exec(fp.read(), version) + + +def get_long_description(): + readme_md = CURRENT_DIR / "README.md" + with open(readme_md, encoding="utf8") as ld_file: + return ld_file.read() + + +setup( + name="eksi", + version=version["__version__"], + description="Komut satırında Ekşisözlük!", + keywords=["ekşisözlük", "ekşi", "eksi", "sözlük"], + long_description=get_long_description(), + long_description_content_type="text/markdown", + author="Furkan Onder", + author_email="furkanonder@protonmail.com", + url="https://github.com/furkanonder/eksigundem/", + license="MIT", + python_requires=">=3.0", + packages=["eksi"], + install_requires=[ + "lxml==4.6.3", + "beautifulsoup4==4.9.3", + "colorama==0.4.4", + ], + extras_require={}, + zip_safe=False, + include_package_data=False, + classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: End Users/Desktop", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Topic :: Utilities", + "Environment :: Console", + "Operating System :: OS Independent", + ], + entry_points={ + "console_scripts": [ + "eksi = eksi.__main__:main", + ] + }, +)