diff --git a/NOTICE.md b/NOTICE.md index 399428b..4326493 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -3,5 +3,7 @@ Searchor is dependent on a lot of different modules in Searchor's codebase. This notice links modules's licenses here that were able to be found that Searchor is dependent on. [`pyperclip`](https://github.com/asweigart/pyperclip/blob/master/LICENSE.txt) +[`aenum`](https://github.com/ethanfurman/aenum/blob/master/aenum/LICENSE) +[`click`](https://github.com/pallets/click/blob/main/LICENSE.rst) Don't see a module's license here but you know the license URL of the module? [Submit a pull request](https://github.com/ArjunSharda/Searchor/pulls)! diff --git a/README.md b/README.md index 9cba0fb..ce336f2 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,16 @@ Quick Start >>> Engine.Google.search("Hello, World!") 'https://www.google.com/search?q=Hello%2C%20World%21' ``` - +Custom Engine +------------- +```python +>>> from searchor import Engine +>>> Engine.new("Colgate", "https://www.colgate.com/en-us/search/all?term=") +>>> print(Engine.Colgate.search("Hi world!", copy_url=True) +'https://www.colgate.com/en-us/search/all?term=Hi%20world!" +``` Searchor CLI Quick Start ---------- -```shell +```bash $ searchor Google "Hello World!" --copy ```
@@ -47,9 +53,12 @@ Take a look at more examples in the [examples](https://github.com/ArjunSharda/Se *Note*:  Engine names follow the **UpperCamelCase** convention.(eg: ChromeWebStore). -v2.3.2 Changes +v2.4.0 Changes -------------- -- **[PATCHED]** Patched a bug with Amazon Web Services, Altassian, and Amazon being duplicates. +- **[ADDED]** Added custom Engine support, check out a example [here](https://github.com/ArjunSharda/Searchor/blob/main/examples/custom_engine.py) +- **[ADDED]** Added Reuters search engine +- **[ADDED]** Added History CLI command which stores data in a JSON file, and which allows you to view, update, and clear. You can see some information of what was added for the history command [here](https://github.com/ArjunSharda/Searchor/pull/125) +- **[MODIFIED]** Modified the CLI with some other minor changes Migration --------- diff --git a/examples/cli/SUPPORTED_OPTIONS.md b/examples/cli/SUPPORTED_OPTIONS.md index 50fa451..f244c6c 100644 --- a/examples/cli/SUPPORTED_OPTIONS.md +++ b/examples/cli/SUPPORTED_OPTIONS.md @@ -1,9 +1,30 @@ # Supported Options -Searchor uses all of the Engines listed in the __init__.py in the CLI as well! +This is a list of all the official supported CLI options of Searchor, with a example of them being used. -# Copy URL -You can do `$ searchor Google "Hello World!" --copy` as an example +# Normal ✅ +```shell +$ searchor search Yahoo "Hello World!" -# Open Web -You can do `$ searchor Target "Hello World!" --open` as an example +``` + +# Copy URL ✅ +```shell +$ searchor search Google "Hello World!" --copy +``` + +# Open Web ✅ + +```shell +$ searchor search Target "Hello World!" --open +``` + +# History ✅ +```shell +$ searchor history +``` + +# Clear History ✅ +```shell +$ searchor history --clear +``` diff --git a/examples/custom_engine.py b/examples/custom_engine.py new file mode 100644 index 0000000..54e3e9f --- /dev/null +++ b/examples/custom_engine.py @@ -0,0 +1,5 @@ +from searchor import Engine + +Engine.new("Custom", "https://www.example.com/search?q=") + +print(Engine.Custom.search("Hello, World!")) diff --git a/pyproject.toml b/pyproject.toml index 8663b80..21a6464 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "searchor" -version = "2.3.2" +version = "2.4.0" authors = [ { name="Arjun Sharda", email="sharda.aj17@gmail.com" }, { name="William Jackson", email="william@kegnh.com" }, diff --git a/setup.py b/setup.py index 1d53d75..f86a115 100644 --- a/setup.py +++ b/setup.py @@ -2,13 +2,11 @@ import pathlib project_dir = pathlib.Path(__file__).parent.resolve() - -# Get the long description from the README file long_description = (project_dir / "README.md").read_text(encoding="utf-8") setup( name="searchor", - version="2.3.2", + version="2.4.0", description="⚡️ Quick and easy search engine queries.", long_description=long_description, long_description_content_type="text/markdown", @@ -21,41 +19,17 @@ "Operating System :: OS Independent", ], - #keywords="sample, setuptools, development", package_dir={"": "src"}, - # You can just specify package directories manually here if your project is - # simple. Or you can use find_packages(). - # - # Alternatively, if you just want to distribute a single Python file, use - # the `py_modules` argument instead as follows, which will expect a file - # called `my_module.py` to exist: - # - # py_modules=["my_module"], - # packages=find_packages(where="src"), # Required python_requires=">=3.7, <4", - install_requires=["pyperclip"], - - # List additional groups of dependencies here (e.g. development - # dependencies). Users will be able to install these using the "extras" - # syntax, for example: - # - # $ pip install sampleproject[dev] - # - # Similar to `install_requires` above, these must be valid existing - # projects. - - #extras_require={ # Optional - # "dev": ["check-manifest"], - # "test": ["coverage"], - #}, + install_requires=["pyperclip", "aenum", "click"], + - # For example, the following would provide a command called `sample` which - # executes the function `main` from this package when invoked: - entry_points={ # Optional + + entry_points={ "console_scripts": [ "searchor=searchor.main:cli", ], diff --git a/src/searchor/__init__.py b/src/searchor/__init__.py index 6618bf7..6809db0 100644 --- a/src/searchor/__init__.py +++ b/src/searchor/__init__.py @@ -1,10 +1,11 @@ from urllib.parse import quote from webbrowser import open_new_tab from enum import Enum, unique +from aenum import extend_enum import pyperclip -@unique +@unique class Engine(Enum): Accuweather = "https://www.accuweather.com/en/search-locations?query={query}" AlternativeTo = "https://alternativeto.net/browse/search/?q={query}" @@ -79,6 +80,7 @@ class Engine(Enum): Quora = "https://www.quora.com/search?q={query}" Reddit = "https://www.reddit.com/search/?q={query}" Replit = "https://replit.com/search?q={query}" + Reuters = "https://www.reuters.com/search/news?blob={query}" Samsung = "https://www.samsung.com/us/search/searchMain/?listType=g&searchTerm={query}" Spotify = "https://open.spotify.com/search/{query}" StackOverflow = "https://www.stackoverflow.com/search?q={query}" @@ -104,6 +106,9 @@ class Engine(Enum): Yandex = "https://yandex.com/search/?text={query}" Youtube = "https://www.youtube.com/results?search_query={query}" + def new(engine_name, base_url): + extend_enum(Engine, engine_name, base_url + "{query}") + def search(self, query, open_web=False, copy_url=False, additional_queries: dict = None): url = self.value.format(query=quote(query, safe="")) if additional_queries: @@ -118,4 +123,3 @@ def search(self, query, open_web=False, copy_url=False, additional_queries: dict pyperclip.copy(url) return url - diff --git a/src/searchor/history.py b/src/searchor/history.py new file mode 100644 index 0000000..de864dc --- /dev/null +++ b/src/searchor/history.py @@ -0,0 +1,38 @@ +import os +import json +import os.path +from datetime import datetime + +DATA_PATH = os.path.join(os.getenv("HOME"), ".searchor-history.json") +tmp = {"searches": []} + + +def update(engine, query, url): + search_data = { + "url": url, + "engine": engine, + "query": query, + "time": str(datetime.today().strftime("%I:%M %p")), + } + + if not os.path.exists(DATA_PATH): # check if data file does not exist + with open(DATA_PATH, "w") as history_file: + json.dump(tmp, history_file) # create file if it does not exist + with open(DATA_PATH, "+r") as history_file: + history_data = json.load(history_file) + history_data["searches"].append(search_data) + history_file.seek(0) + json.dump(history_data, history_file, indent=4) + + +def clear(): + if os.path.exists(DATA_PATH): + with open(DATA_PATH, "w") as history_file: + json.dump(tmp, history_file) + + +def view(): + with open(DATA_PATH, "+r") as history_file: + history_data = json.load(history_file) + for search in history_data["searches"]: + print(f"{search['time']}: {search['url']}") diff --git a/src/searchor/main.py b/src/searchor/main.py index f4d45d3..9a35010 100644 --- a/src/searchor/main.py +++ b/src/searchor/main.py @@ -1,8 +1,14 @@ import click from searchor import Engine +import searchor.history -@click.command() +@click.group() +def cli(): + pass + + +@cli.command() @click.option( "-o", "--open", @@ -21,14 +27,35 @@ ) @click.argument("engine") @click.argument("query") -def cli(engine, query, open, copy): - click.echo( - eval(f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})") - ) - if open: - click.echo("opening browser...") - if copy: - click.echo("link copied to clipboard") +def search(engine, query, open, copy): + try: + url = eval( + f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})" + ) + click.echo(url) + searchor.history.update(engine, query, url) + if open: + click.echo("opening browser...") + if copy: + click.echo("link copied to clipboard") + except AttributeError: + print("engine not recognized") + + +@cli.command() +@click.option( + "-c", + "--clear", + is_flag=True, + default=False, + show_default=True, + help="clears the search history", +) +def history(clear): + if clear: + searchor.history.clear() + else: + searchor.history.view() if __name__ == "__main__":