Name | pycallblock JSON |
Version |
1.1.0
JSON |
| download |
home_page | |
Summary | Block spam calls with a USB modem. |
upload_time | 2023-11-12 18:34:30 |
maintainer | |
docs_url | None |
author | |
requires_python | >=3.9 |
license | |
keywords |
modem
block
spam
phone
call
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# pycallblock
<p align="left">
<a><img alt="Python versions" src="https://img.shields.io/pypi/pyversions/pycallblock"></a>
<a href="https://pypi.org/project/pycallblock/"><img alt="PyPI" src="https://img.shields.io/pypi/v/pycallblock"></a>
<a href="https://github.com/AT0myks/pycallblock/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/pycallblock"></a>
</p>
* [Introduction](#introduction)
* [Requirements](#requirements)
* [Installation](#installation)
* [Usage](#usage)
* [Block modes](#block-modes)
* [Extended functionality](#extended-functionality)
* [Notes](#notes)
* [Glossary](#glossary)
## Introduction
This project started in July 2020 after I finally decided
to do something about those daily spam calls.
I found [jcblock](https://sourceforge.net/projects/jcblock/),
which inspired me to make my own version of a call blocker.
So I bought a cheap modem and found a manual online.
I had no idea how all of this worked and it took some time to get used
to the modem and how to properly use it.
Seeing the caller ID show up for the first time in the terminal was very satisfying.
Then I found out that you can also use the modem to record the calls
and even send audio, and you basically get a phone controlled by software.
After a few years of successful blocking and some upgrades,
it's time for this project to be made public so that you can enjoy a silent
phone while the scammers are listening to the audio files you prepared for them.
## Requirements
### Hardware
You need a computer and a USB modem. I use a Raspberry Pi Zero.
In theory it should work with any
[AT](https://en.wikipedia.org/wiki/Hayes_command_set) modem,
but I have not tested anything else than the Conexant based ones I have,
so I cannot guarantee that different models will work.
The modem must support caller ID, and voice mode if you want to use the voice features.
Unfortunately you can't really be sure of the modem's capabilities before you plug it in.
Only hardware modems are supported, this is not compatible with softmodems.
Here are the results of `lsusb` for two modems that I have:
```
0572:1340 Conexant Systems (Rockwell), Inc. USB Modem
0572:1340 Conexant Systems (Rockwell), Inc.
```
They both look like [this](https://www.aliexpress.com/item/32974192089.html).
It used to be easy to find them for pretty cheap on sites
like eBay and AliExpress but it seems like
that's not the case anymore, unfortunately.
If you have a wireless phone I guess you can use a one port modem.
If your phone is wired you'll need a modem with two ports.
Once you plug it in, the modem should show up as something like `/dev/ttyACM0`
(this the default device in pycallblock).
Installing pycallblock will install
[pyserial](https://github.com/pyserial/pyserial)
which comes with
[`pyserial-ports`](https://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.list_ports)
and
[`pyserial-miniterm`](https://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.miniterm).
You can use this first command to find serial ports
and the second one to test your device.
Here's the output in my case:
```
$ pyserial-ports -v
/dev/ttyACM0
desc: USB Modem
hwid: USB VID:PID=0572:1340 SER=12345678 LOCATION=1-1:1.0
```
### Software
Linux and Python 3.9+.
I did not test it on Windows, but if you can interface with the modem
just like you can do on Linux, and if the code is compatible,
there should be no reason for it not to work.
On Linux you'll probably have to add your user to the `dialout` group
by doing `sudo usermod -a -G dialout youruser` and then logging out and in again.
## Installation
```
pip install pycallblock
```
## Usage
pycallblock comes with a CLI:
```
optional arguments:
-h, --help show this help message and exit
-V, --version print version
--device DEV the device to use. Default: /dev/ttyACM0
--logfile FILE enable logging to the specified file
--syslog [ADDRESS] enable logging to syslog. Default: /dev/log
-p, --block-private block private numbers. Default: false
-l, --stderr log to stderr
-s SEC, --silence SEC seconds of silence before triggering the callback. Only for duplex. Must be an int or a float > 0. Default: disabled
-v, --verbose increase verbosity
-a FILE, --audio-file FILE WAV file for -m, -d and -t modes
--timezone TIMEZONE IANA timezone for the file names. Default: UTC
-b CSV, --blacklist CSV CSV file with the contacts that will be blocked
-w CSV, --whitelist CSV CSV file with the contacts that will be allowed
Block actions:
Choose what happens when a call is blocked. If nothing is specified, default to instant hangup.
-f, --fax act as a fax machine
-m, --message play a message specified with -a
-r [SEC], --record [SEC] record the call for SEC seconds. Default: 120
-d [SEC], --duplex [SEC] duplex for SEC seconds. Default: 120
-t [SEC], --transmit [SEC] transmit for SEC seconds. Default: 120
```
When you run pycallblock for the first time, it will create a `pycallblock`
directory in the user's home.
Inside, the database for logging the calls will be created
along with the `play` and `rec` directories.
The recordings will go in `rec`, and in `play` you can put your WAV files
to be randomly sent in duplex mode.
This is also where you can put your CSV file that contains blocked/allowed contacts.
The expected separator for the CSV file is the comma.
If you don't have a name for a contact, you must still put a comma after the number.
If you want to have a comma in the name, use double quotes around the name.
Example:
```
01234,
56789,Ben
5055034455,"Look, there's a comma"
```
If your block/allow list is empty, every call will be allowed/blocked respectively.
The recordings will be named according to the following format:
`%Y-%m-%d %H-%M-%S%z NUMBER`. The default timezone is UTC.
If you want your files to be named according to your local time,
set a [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
with `--timezone`.
On Windows you might need the [tzdata](https://pypi.org/project/tzdata/) package.
### Recording WAV files
The audio must be unsigned PCM, mono, 8bit, at 8000Hz.
I use [Audacity](https://www.audacityteam.org/) to record the WAV files.
In the bottom left corner set the `Project Rate` to 8000Hz,
and record your audio.
When exporting, choose `Other uncompressed files` then `WAV (Microsoft)`
and `Unsigned 8-bit PCM` and set the output channels to 1.
Then you can put the file in `play` and test it to see if it plays correctly.
It's also possible to use Audacity to convert an existing file by going to
`Tracks -> Resample...`, choosing 8000Hz and exporting as described above.
### Run as a service
See the [example file](https://github.com/AT0myks/pycallblock/blob/main/pycallblock.service).
You can add the `--syslog` argument to the command and then do
`journalctl -t pycallblock` or `journalctl -u pycallblock` to see the logs.
Do not use both `--syslog` and `--stderr` in this case.
## Block modes
### Default behaviour
Instantly hang up.
The caller's device might keep ringing,
but nothing should happen on your end.
Adding a delay before hanging up should help, in case that's what you want.
### Fax machine
The modem will act as a fax machine and play the
[CED tone](https://www.youtube.com/watch?v=6v4GDjenyZE).
### Play an audio file
Play a WAV file and hang up.
### Transmit
Send audio until the timeout is reached or the caller hangs up.
If you want to play an audio file after picking up, use the `-a` option.
In this mode, DTMFs are received.
This means that you could for example execute a certain action
(like playing a WAV file) when a specific button is pressed,
just like [IVR](https://en.wikipedia.org/wiki/Interactive_voice_response).
Not the most interesting mode for blocking calls, but it might have its use cases.
### Record
Record the call until the timeout is reached or the caller hangs up.
DTMFs are received, but because there's no transmit,
you can't send any audio back.
### Duplex
Record + transmit. Recording alone can already give entertaining results,
but sometimes if there's no sound after the robot picks up, it will drop the call.
Like with transmit, you can set an audio file to be played at pick up.
If you enable silence detection with `-s`, a random file from the
`play` directory will be played after the specified amount of seconds
of silence has been reached.
The recording will contain the audio that is sent.
## Extended functionality
The default functionality should already be enough for most.
But you can also build on top of pycallblock to make your own call blocker.
If you choose to do this, you'll have to write your own code to run the program.
It's only a few lines that you can find at the bottom of
[`__init__.py`](https://github.com/AT0myks/pycallblock/blob/main/pycallblock/__init__.py).
### Callbacks
The main way is by making your own subclass of `Callblock`
and overriding the callbacks.
```py
from pycallblock import Callblock
class MyCallblock(Callblock):
async def ring_callback(self, event): # awaited in the modem's event loop
# Choose what happens when the phone rings
async def call_callback(self, call): # awaited in the modem's event loop
# Choose what happens when you receive a call
async def mesg_callback(self, event): # awaited in the modem's event loop
# Choose what happens when you receive a MESG event
async def dtmf_callback(self, dtmf): # runs as a task
# Choose what happens when you receive a DTMF during a call
async def silence_callback(self): # runs as a task
# Choose what happens when silence has been detected
```
In case you want to reuse the same CLI and options, two functions are available:
```py
from pycallblock import cli, options_from_args
from pycallblock.modem import Modem
args = cli()
options = options_from_args(args)
async with Modem("/dev/ttyACM0") as modem:
mycallblock = MyCallblock(modem, ..., options=options)
```
### Also, you can make calls
```py
from pycallblock.modem import Modem, Mode
async with Modem("/dev/ttyACM0") as modem:
wav = "outgoing.wav" # Record to a file
await modem.set_mode(Mode.VOICE)
await modem.send("AT+VSM=1")
await modem.start_voice_call("5055034455", wav=wav, max_duration=30)
await modem.voice_end()
await modem.hang_up()
```
## Notes
Here are some notes in no particular order.
- there is always one ring before the caller ID is sent
- your phone might have a setting to disable the first ring
- sometimes the phone still rings a few times after the call is blocked
- depending on your hardware, the recordings may contain annoying background noises
- the modem may report "fake" DSCs (this is a limitation of the way the modem works)
- I've had instances where the modem becomes unresponsive and I have to restart the Pi,
or the Pi also crashes and reboots
- sometimes the modem won't send BUSY after the caller hangs up,
in that case the call will last as long as the timeout
- for silence detection, as soon as there is noise the timer is reset
- in the modes where TX is active,
you'll always see one `Received DSC: Transmit Buffer Underrun` in the logs
- each call to `Modem.send_audio_file` puts the audio in a queue.
Each file is played one by one.
If the queue is not empty when the call is ending,
the remaining files in the queue won't be played
- `Callblock.start` installs a signal handler for `SIGINT` and `SIGTERM`.
When one of these signals is received,
the pycallblock instance will be marked as not running
- if a call is in progress when the program is exiting,
the call will terminate as soon as possible
- the modem's green LED turns on while a call is in progress
- the `+VSD` command for silence detection exists but does nothing,
so I implemented a very basic silence detection on my own
- I mostly used [this reference manual](https://web.archive.org/web/20160201002959/http://www.xmodus.ch/Downloads/XM3000S/XM3000S-A00-103.pdf)
- the country codes (unused for now) come from [this one](https://datasheet.octopart.com/CX93010-21Z-Conexant-datasheet-20734837.pdf)
## Glossary
- DCE: [Data circuit-terminating equipment](https://en.wikipedia.org/wiki/Data_circuit-terminating_equipment)
- DLE: Data link escape
- DSC: DLE shielded code.
- DTE: Data terminal equipment
- DTMF: [Dual-tone multi-frequency](https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling)
- PVF: Portable voice format
- RMD: Raw modem data
- RX: Receive
- TX: Transmit
Raw data
{
"_id": null,
"home_page": "",
"name": "pycallblock",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": "",
"keywords": "modem,block,spam,phone,call",
"author": "",
"author_email": "AT0myks <at0myks.dev@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/5f/d9/ac0b4b53b0f01036f095337f06b7cbcb6cdc2746f3916e3898cc00635235/pycallblock-1.1.0.tar.gz",
"platform": null,
"description": "# pycallblock\n\n<p align=\"left\">\n<a><img alt=\"Python versions\" src=\"https://img.shields.io/pypi/pyversions/pycallblock\"></a>\n<a href=\"https://pypi.org/project/pycallblock/\"><img alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/pycallblock\"></a>\n<a href=\"https://github.com/AT0myks/pycallblock/blob/main/LICENSE\"><img alt=\"License\" src=\"https://img.shields.io/pypi/l/pycallblock\"></a>\n</p>\n\n* [Introduction](#introduction)\n* [Requirements](#requirements)\n* [Installation](#installation)\n* [Usage](#usage)\n* [Block modes](#block-modes)\n* [Extended functionality](#extended-functionality)\n* [Notes](#notes)\n* [Glossary](#glossary)\n\n## Introduction\n\nThis project started in July 2020 after I finally decided\nto do something about those daily spam calls.\nI found [jcblock](https://sourceforge.net/projects/jcblock/),\nwhich inspired me to make my own version of a call blocker.\nSo I bought a cheap modem and found a manual online.\nI had no idea how all of this worked and it took some time to get used\nto the modem and how to properly use it.\nSeeing the caller ID show up for the first time in the terminal was very satisfying.\nThen I found out that you can also use the modem to record the calls\nand even send audio, and you basically get a phone controlled by software.\nAfter a few years of successful blocking and some upgrades,\nit's time for this project to be made public so that you can enjoy a silent\nphone while the scammers are listening to the audio files you prepared for them.\n\n## Requirements\n\n### Hardware\n\nYou need a computer and a USB modem. I use a Raspberry Pi Zero.\nIn theory it should work with any\n[AT](https://en.wikipedia.org/wiki/Hayes_command_set) modem,\nbut I have not tested anything else than the Conexant based ones I have,\nso I cannot guarantee that different models will work.\nThe modem must support caller ID, and voice mode if you want to use the voice features.\nUnfortunately you can't really be sure of the modem's capabilities before you plug it in.\n\nOnly hardware modems are supported, this is not compatible with softmodems.\n\nHere are the results of `lsusb` for two modems that I have:\n```\n0572:1340 Conexant Systems (Rockwell), Inc. USB Modem\n0572:1340 Conexant Systems (Rockwell), Inc.\n```\nThey both look like [this](https://www.aliexpress.com/item/32974192089.html).\nIt used to be easy to find them for pretty cheap on sites\nlike eBay and AliExpress but it seems like\nthat's not the case anymore, unfortunately.\n\nIf you have a wireless phone I guess you can use a one port modem.\nIf your phone is wired you'll need a modem with two ports.\n\nOnce you plug it in, the modem should show up as something like `/dev/ttyACM0`\n(this the default device in pycallblock).\n\nInstalling pycallblock will install\n[pyserial](https://github.com/pyserial/pyserial)\nwhich comes with \n[`pyserial-ports`](https://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.list_ports)\nand\n[`pyserial-miniterm`](https://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.miniterm).\nYou can use this first command to find serial ports\nand the second one to test your device.\n\nHere's the output in my case:\n```\n$ pyserial-ports -v\n/dev/ttyACM0\n desc: USB Modem\n hwid: USB VID:PID=0572:1340 SER=12345678 LOCATION=1-1:1.0\n```\n\n### Software\n\nLinux and Python 3.9+.\n\nI did not test it on Windows, but if you can interface with the modem\njust like you can do on Linux, and if the code is compatible,\nthere should be no reason for it not to work.\nOn Linux you'll probably have to add your user to the `dialout` group\nby doing `sudo usermod -a -G dialout youruser` and then logging out and in again.\n\n## Installation\n\n```\npip install pycallblock\n```\n\n## Usage\n\npycallblock comes with a CLI:\n\n```\noptional arguments:\n -h, --help show this help message and exit\n -V, --version print version\n --device DEV the device to use. Default: /dev/ttyACM0\n --logfile FILE enable logging to the specified file\n --syslog [ADDRESS] enable logging to syslog. Default: /dev/log\n -p, --block-private block private numbers. Default: false\n -l, --stderr log to stderr\n -s SEC, --silence SEC seconds of silence before triggering the callback. Only for duplex. Must be an int or a float > 0. Default: disabled\n -v, --verbose increase verbosity\n -a FILE, --audio-file FILE WAV file for -m, -d and -t modes\n --timezone TIMEZONE IANA timezone for the file names. Default: UTC\n -b CSV, --blacklist CSV CSV file with the contacts that will be blocked\n -w CSV, --whitelist CSV CSV file with the contacts that will be allowed\n\nBlock actions:\n Choose what happens when a call is blocked. If nothing is specified, default to instant hangup.\n\n -f, --fax act as a fax machine\n -m, --message play a message specified with -a\n -r [SEC], --record [SEC] record the call for SEC seconds. Default: 120\n -d [SEC], --duplex [SEC] duplex for SEC seconds. Default: 120\n -t [SEC], --transmit [SEC] transmit for SEC seconds. Default: 120\n```\n\nWhen you run pycallblock for the first time, it will create a `pycallblock`\ndirectory in the user's home.\nInside, the database for logging the calls will be created\nalong with the `play` and `rec` directories.\nThe recordings will go in `rec`, and in `play` you can put your WAV files\nto be randomly sent in duplex mode.\nThis is also where you can put your CSV file that contains blocked/allowed contacts.\nThe expected separator for the CSV file is the comma.\nIf you don't have a name for a contact, you must still put a comma after the number.\nIf you want to have a comma in the name, use double quotes around the name.\nExample:\n```\n01234,\n56789,Ben\n5055034455,\"Look, there's a comma\"\n```\nIf your block/allow list is empty, every call will be allowed/blocked respectively.\n\nThe recordings will be named according to the following format:\n`%Y-%m-%d %H-%M-%S%z NUMBER`. The default timezone is UTC.\nIf you want your files to be named according to your local time,\nset a [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)\nwith `--timezone`.\nOn Windows you might need the [tzdata](https://pypi.org/project/tzdata/) package.\n\n### Recording WAV files\n\nThe audio must be unsigned PCM, mono, 8bit, at 8000Hz.\nI use [Audacity](https://www.audacityteam.org/) to record the WAV files.\nIn the bottom left corner set the `Project Rate` to 8000Hz,\nand record your audio.\nWhen exporting, choose `Other uncompressed files` then `WAV (Microsoft)`\nand `Unsigned 8-bit PCM` and set the output channels to 1.\nThen you can put the file in `play` and test it to see if it plays correctly.\n\nIt's also possible to use Audacity to convert an existing file by going to\n`Tracks -> Resample...`, choosing 8000Hz and exporting as described above.\n\n### Run as a service\n\nSee the [example file](https://github.com/AT0myks/pycallblock/blob/main/pycallblock.service).\nYou can add the `--syslog` argument to the command and then do\n`journalctl -t pycallblock` or `journalctl -u pycallblock` to see the logs.\nDo not use both `--syslog` and `--stderr` in this case.\n\n## Block modes\n\n### Default behaviour\n\nInstantly hang up.\nThe caller's device might keep ringing,\nbut nothing should happen on your end.\nAdding a delay before hanging up should help, in case that's what you want.\n\n### Fax machine\n\nThe modem will act as a fax machine and play the\n[CED tone](https://www.youtube.com/watch?v=6v4GDjenyZE).\n\n### Play an audio file\n\nPlay a WAV file and hang up.\n\n### Transmit\n\nSend audio until the timeout is reached or the caller hangs up.\nIf you want to play an audio file after picking up, use the `-a` option.\nIn this mode, DTMFs are received.\nThis means that you could for example execute a certain action\n(like playing a WAV file) when a specific button is pressed,\njust like [IVR](https://en.wikipedia.org/wiki/Interactive_voice_response).\nNot the most interesting mode for blocking calls, but it might have its use cases.\n\n### Record\n\nRecord the call until the timeout is reached or the caller hangs up.\nDTMFs are received, but because there's no transmit,\nyou can't send any audio back.\n\n### Duplex\n\nRecord + transmit. Recording alone can already give entertaining results,\nbut sometimes if there's no sound after the robot picks up, it will drop the call.\nLike with transmit, you can set an audio file to be played at pick up.\nIf you enable silence detection with `-s`, a random file from the \n`play` directory will be played after the specified amount of seconds\nof silence has been reached.\nThe recording will contain the audio that is sent.\n\n## Extended functionality\n\nThe default functionality should already be enough for most.\nBut you can also build on top of pycallblock to make your own call blocker.\n\nIf you choose to do this, you'll have to write your own code to run the program.\nIt's only a few lines that you can find at the bottom of\n[`__init__.py`](https://github.com/AT0myks/pycallblock/blob/main/pycallblock/__init__.py).\n\n### Callbacks\n\nThe main way is by making your own subclass of `Callblock`\nand overriding the callbacks.\n\n```py\nfrom pycallblock import Callblock\n\nclass MyCallblock(Callblock):\n\n async def ring_callback(self, event): # awaited in the modem's event loop\n # Choose what happens when the phone rings\n\n async def call_callback(self, call): # awaited in the modem's event loop\n # Choose what happens when you receive a call\n\n async def mesg_callback(self, event): # awaited in the modem's event loop\n # Choose what happens when you receive a MESG event\n\n async def dtmf_callback(self, dtmf): # runs as a task\n # Choose what happens when you receive a DTMF during a call\n\n async def silence_callback(self): # runs as a task\n # Choose what happens when silence has been detected\n```\nIn case you want to reuse the same CLI and options, two functions are available:\n```py\nfrom pycallblock import cli, options_from_args\nfrom pycallblock.modem import Modem\n\nargs = cli()\noptions = options_from_args(args)\nasync with Modem(\"/dev/ttyACM0\") as modem:\n mycallblock = MyCallblock(modem, ..., options=options)\n```\n\n### Also, you can make calls\n\n```py\nfrom pycallblock.modem import Modem, Mode\n\nasync with Modem(\"/dev/ttyACM0\") as modem:\n wav = \"outgoing.wav\" # Record to a file\n await modem.set_mode(Mode.VOICE)\n await modem.send(\"AT+VSM=1\")\n await modem.start_voice_call(\"5055034455\", wav=wav, max_duration=30)\n await modem.voice_end()\n await modem.hang_up()\n```\n\n## Notes\n\nHere are some notes in no particular order.\n\n- there is always one ring before the caller ID is sent\n- your phone might have a setting to disable the first ring\n- sometimes the phone still rings a few times after the call is blocked\n- depending on your hardware, the recordings may contain annoying background noises\n- the modem may report \"fake\" DSCs (this is a limitation of the way the modem works)\n- I've had instances where the modem becomes unresponsive and I have to restart the Pi,\nor the Pi also crashes and reboots\n- sometimes the modem won't send BUSY after the caller hangs up,\nin that case the call will last as long as the timeout\n- for silence detection, as soon as there is noise the timer is reset\n- in the modes where TX is active,\nyou'll always see one `Received DSC: Transmit Buffer Underrun` in the logs\n- each call to `Modem.send_audio_file` puts the audio in a queue.\nEach file is played one by one.\nIf the queue is not empty when the call is ending,\nthe remaining files in the queue won't be played\n- `Callblock.start` installs a signal handler for `SIGINT` and `SIGTERM`.\nWhen one of these signals is received,\nthe pycallblock instance will be marked as not running\n- if a call is in progress when the program is exiting,\nthe call will terminate as soon as possible\n- the modem's green LED turns on while a call is in progress\n- the `+VSD` command for silence detection exists but does nothing,\nso I implemented a very basic silence detection on my own\n- I mostly used [this reference manual](https://web.archive.org/web/20160201002959/http://www.xmodus.ch/Downloads/XM3000S/XM3000S-A00-103.pdf)\n- the country codes (unused for now) come from [this one](https://datasheet.octopart.com/CX93010-21Z-Conexant-datasheet-20734837.pdf)\n\n## Glossary\n- DCE: [Data circuit-terminating equipment](https://en.wikipedia.org/wiki/Data_circuit-terminating_equipment)\n- DLE: Data link escape\n- DSC: DLE shielded code.\n- DTE: Data terminal equipment\n- DTMF: [Dual-tone multi-frequency](https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling)\n- PVF: Portable voice format\n- RMD: Raw modem data\n- RX: Receive\n- TX: Transmit\n",
"bugtrack_url": null,
"license": "",
"summary": "Block spam calls with a USB modem.",
"version": "1.1.0",
"project_urls": {
"Issues": "https://github.com/AT0myks/pycallblock/issues",
"Source": "https://github.com/AT0myks/pycallblock"
},
"split_keywords": [
"modem",
"block",
"spam",
"phone",
"call"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "af357309c3915cc8a22c6bd8cdafe967124ccb781feedaa4cbd3229c95acfa93",
"md5": "874fd3dd265c66eb8f4de336c11d9270",
"sha256": "70e64e37c478c1bdc828ccb2ab12ef47e9f4bf04c716fcf3e44b71e8adfc9dd6"
},
"downloads": -1,
"filename": "pycallblock-1.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "874fd3dd265c66eb8f4de336c11d9270",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 31424,
"upload_time": "2023-11-12T18:34:29",
"upload_time_iso_8601": "2023-11-12T18:34:29.074245Z",
"url": "https://files.pythonhosted.org/packages/af/35/7309c3915cc8a22c6bd8cdafe967124ccb781feedaa4cbd3229c95acfa93/pycallblock-1.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "5fd9ac0b4b53b0f01036f095337f06b7cbcb6cdc2746f3916e3898cc00635235",
"md5": "2ed83f7fdb6cd02db4db94f83790fe10",
"sha256": "1413288b890823c113a8801b03060fbcfef3e388ed4a6395b4eab91e01a5866c"
},
"downloads": -1,
"filename": "pycallblock-1.1.0.tar.gz",
"has_sig": false,
"md5_digest": "2ed83f7fdb6cd02db4db94f83790fe10",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 35191,
"upload_time": "2023-11-12T18:34:30",
"upload_time_iso_8601": "2023-11-12T18:34:30.568682Z",
"url": "https://files.pythonhosted.org/packages/5f/d9/ac0b4b53b0f01036f095337f06b7cbcb6cdc2746f3916e3898cc00635235/pycallblock-1.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-11-12 18:34:30",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "AT0myks",
"github_project": "pycallblock",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "pycallblock"
}