{"id":24059714,"url":"https://github.com/novitae/emdofi","last_synced_at":"2025-04-23T03:41:02.328Z","repository":{"id":139489885,"uuid":"553577631","full_name":"novitae/emdofi","owner":"novitae","description":"🔦 Uncovers a censored email's domain","archived":false,"fork":false,"pushed_at":"2023-06-15T10:07:21.000Z","size":227,"stargazers_count":38,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T21:41:25.564Z","etag":null,"topics":["censored","domain","email","osint","socmint","toutatis"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/novitae.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-10-18T12:35:23.000Z","updated_at":"2025-03-24T00:53:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"fdad1d5d-ef23-4bcb-b2b1-4ca03da61558","html_url":"https://github.com/novitae/emdofi","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/novitae%2Femdofi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/novitae%2Femdofi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/novitae%2Femdofi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/novitae%2Femdofi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/novitae","download_url":"https://codeload.github.com/novitae/emdofi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250366659,"owners_count":21418767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["censored","domain","email","osint","socmint","toutatis"],"created_at":"2025-01-09T06:18:06.180Z","updated_at":"2025-04-23T03:41:02.321Z","avatar_url":"https://github.com/novitae.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🔦 EMDOFI - Censored domains uncovering\n**⚠️ Any deviations from the license will result consequences. Contact me if you have doubts.**\n\n---\n![](./screenshot.png)\n## ⬇️ Installation\n(you can copy the whole line)\n```py\npip install git+https://github.com/novitae/emdofi.git\n```\n## 💻 Usage as a command line\n`emdofi \u003cdomain or email\u003e [-h] [-c CENSORED_CHARS]`\n```\nusage: EMDOFI - Uncover a censored domain [-h] [-c CC] domain\n\npositional arguments:\n  domain                The censored domain or email\n\noptions:\n  -h, --help            show this help message and exit\n  -c CC, --censored CC  The censored characters (default: \"*?\")\n```\n## 📚 Usage as a library\n### `match`\nThis is the simplest action, just making a match, without having to load a lot of stuff.\n```py\n\u003e\u003e\u003e from emdofi import match\n\u003e\u003e\u003e match(\"*******@c****.c*********m\")\n[cable.comcast.com]\n\u003e\u003e\u003e # You can also define the censored chars with censored_chars.\n\u003e\u003e\u003e # It needs to be an iterable of string.\n\u003e\u003e\u003e match(\"*******@c****.c***????**m\", censored_chars=\"*?\")\n[cable.comcast.com]\n```\n### `DomainFinder`\n`DomainFinder` is the main class that will make the whole work. It gets initialized manually this way:\n```py\n\u003e\u003e\u003e from emdofi import DomainFinder\n\u003e\u003e\u003e d = DomainFinder(\n...     domains = [\"gmail.com\",\"yahoo.com\",\"icloud.com\"],\n...     censoring_chars = \"*?\"\n... )\n\u003e\u003e\u003e # If you disable `keep_only_valid_domains` in `DomainFinder`,\n\u003e\u003e\u003e # the domains that might be not corresponding to a valid\n\u003e\u003e\u003e # domain will be kept.\n```\n**BUT** you can also initialize it with easier ways:\n```py\n\u003e\u003e\u003e # Load the default library of domains, present here:\n\u003e\u003e\u003e # https://gist.github.com/ammarshah/f5c2624d767f91a7cbdc4e54db8dd0bf/\n\u003e\u003e\u003e d = DomainFinder.load_default()\n\u003e\u003e\u003e\n\u003e\u003e\u003e # Load from a file, that must be containing\n\u003e\u003e\u003e # a domain on each line, or is a json list\n\u003e\u003e\u003e # of domains.\n\u003e\u003e\u003e with open(\"file.txt\", \"r\") as read:\n\u003e\u003e\u003e     d = DomainFinder.load(read)\n\u003e\u003e\u003e\n\u003e\u003e\u003e # Load from a string that could be contained\n\u003e\u003e\u003e # in the file upper described.\n\u003e\u003e\u003e d = DomainFinder.loads(\"gmail.com\\nyahoo.com\\nicloud.com\")\n```\nOnce the `DomainFinder` object intialized, you can re-edit the censored chars with:\n```py\n\u003e\u003e\u003e d.change_censoring_chars(\n...     \"*\", \"?\", \"£$?\"\n... )\n\u003e\u003e\u003e # The chars will be unpacked as the following:\n\u003e\u003e\u003e d.change_censoring_chars(\"fffç\u0026\", \"*\", \"$$\u0026\")\n\u003e\u003e\u003e d.censoring_chars\n[\"f\", \"ç\", \"\u0026\", \"*\", \"$\"]\n```\nThen you can start your matches:\n```py\n\u003e\u003e\u003e d.match(\"g****.**m\")\n[\"gmail.com\", \"gmial.com\"]\n\u003e\u003e\u003e # You can use an email, it will only inspect the domain\n\u003e\u003e\u003e d.match(\"john@g****.**m\")\n[\"gmail.com\", \"gmial.com\"]\n\u003e\u003e\u003e # By using the arg `full`, you will get the whole dict\n\u003e\u003e\u003e # such as {domain(str): result(bool), ...}\n\u003e\u003e\u003e d.match(\"g****.**m\", full=True)\n{\"gmail.com\": True, \"...\": False, \"...\": False, \"gmial.com\": True, ...}\n```\n### `CensoredDomain`\nIn the case you would like to match different domains being censored with differents characters each, you should use `CensoredDomain`\n```py\n\u003e\u003e\u003e # Make sure the `censored_chars` is a list of string,\n\u003e\u003e\u003e # they won't be converted, so it can cause errors if\n\u003e\u003e\u003e # they are not from this specific `list[str]` type.\n\u003e\u003e\u003e cd1 = CensoredDomain(\n...     \"john@g****.**m\",\n...     censored_chars = [\"*\"]\n... )\n\u003e\u003e\u003e cd2 = CensoredDomain(\n...     \"john@g$)$$.%%m\",\n...     censored_chars = [\"$\",\")\",\"%\"]\n... )\n\u003e\u003e\u003e for x in [cd1, cd2]:\n...     print(x.domain, d.match(x))\n\"g****.**m\", [\"gmail.com\", \"gmial.com\"]\n\"g$)$$.%%m\", [\"gmail.com\", \"gmial.com\"]\n```\n### `SingleDomain`\nYou can also try to make single matches, using object `SingleDomain`:\n```py\n\u003e\u003e\u003e import asyncio\n\u003e\u003e\u003e sd = SingleDomain(\n...     \"icloud.com\"\n... )\n\u003e\u003e\u003e # SingleDomain also contains an arg `valid`. Put it on\n\u003e\u003e\u003e # `True` or `False` if you want to force its value, its\n\u003e\u003e\u003e # default value is `None`\n\u003e\u003e\u003e \n\u003e\u003e\u003e # Then you can compare it to a `CensoredDomain` object:\n\u003e\u003e\u003e asyncio.run(sd.match(cd1))\nFalse\n\u003e\u003e\u003e # I know, it is boring in async, but it allows to make\n\u003e\u003e\u003e # a bulk comparing within seconds for millions of domains.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnovitae%2Femdofi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnovitae%2Femdofi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnovitae%2Femdofi/lists"}