Private Decentralized Communication Network
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

175 lines
5.9 KiB

  1. """Onionr - Private P2P Communication.
  2. This default plugin handles "flow" messages
  3. (global chatroom style communication)
  4. """
  5. import sys
  6. import os
  7. import deadsimplekv as simplekv
  8. from utils import identifyhome, reconstructhash
  9. from coredb import blockmetadb
  10. import threading
  11. import time
  12. import locale
  13. from onionrblocks.onionrblockapi import Block
  14. import logger
  15. import onionrblocks
  16. from onionrutils import escapeansi, epoch, bytesconverter
  17. locale.setlocale(locale.LC_ALL, '')
  18. sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
  19. # import after path insert
  20. import flowapi # noqa
  21. """
  22. This program is free software: you can redistribute it and/or modify
  23. it under the terms of the GNU General Public License as published by
  24. the Free Software Foundation, either version 3 of the License, or
  25. (at your option) any later version.
  26. This program is distributed in the hope that it will be useful,
  27. but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. GNU General Public License for more details.
  30. You should have received a copy of the GNU General Public License
  31. along with this program. If not, see <https://www.gnu.org/licenses/>.
  32. """
  33. flask_blueprint = flowapi.flask_blueprint
  34. security_whitelist = ['staticfiles.boardContent', 'staticfiles.board']
  35. plugin_name = 'flow'
  36. PLUGIN_VERSION = '0.1.0'
  37. EXPIRE_TIME = 43200
  38. class OnionrFlow:
  39. def __init__(self):
  40. self.alreadyOutputed = []
  41. self.flowRunning = False
  42. self.channel = ""
  43. return
  44. def start(self):
  45. logger.warn(
  46. "Please note: everything said here is public, " +
  47. "even if a random channel name is used.", terminal=True)
  48. message = ""
  49. self.flowRunning = True
  50. try:
  51. self.channel = logger.readline(
  52. "Enter a channel name or none for default:").strip()
  53. except (KeyboardInterrupt, EOFError):
  54. self.flowRunning = False
  55. newThread = threading.Thread(target=self.showOutput, daemon=True)
  56. newThread.start()
  57. while self.flowRunning:
  58. if self.channel == "":
  59. self.channel = "global"
  60. try:
  61. message = logger.readline('\nInsert message into flow:').strip().replace(
  62. '\n', '\\n').replace('\r', '\\r')
  63. except EOFError:
  64. pass
  65. except KeyboardInterrupt:
  66. self.flowRunning = False
  67. else:
  68. if message == "q":
  69. self.flowRunning = False
  70. expireTime = epoch.get_epoch() + EXPIRE_TIME
  71. if len(message) > 0:
  72. logger.info('Inserting message as block...', terminal=True)
  73. onionrblocks.insert(message, header='brd',
  74. expire=expireTime,
  75. meta = {
  76. 'ch': self.channel})
  77. logger.info("Flow is exiting, goodbye", terminal=True)
  78. return
  79. def showOutput(self):
  80. while type(self.channel) is type(None) and self.flowRunning:
  81. time.sleep(1)
  82. try:
  83. while self.flowRunning:
  84. for block in blockmetadb.get_blocks_by_type('brd'):
  85. if block in self.alreadyOutputed:
  86. continue
  87. block = Block(block)
  88. b_hash = bytesconverter.bytes_to_str(block.getHash())
  89. if block.getMetadata('ch') != self.channel:
  90. continue
  91. if not self.flowRunning:
  92. break
  93. logger.info('\n------------------------',
  94. prompt=False, terminal=True)
  95. content = block.getContent()
  96. # Escape new lines, remove trailing whitespace, and escape ansi sequences
  97. content = escapeansi.escape_ANSI(content.replace(
  98. '\n', '\\n').replace('\r', '\\r').strip())
  99. logger.info(block.getDate().strftime(
  100. "%m/%d %H:%M") + ' - ' + logger.colors.reset + content, prompt=False, terminal=True)
  101. self.alreadyOutputed.append(b_hash)
  102. time.sleep(5)
  103. except KeyboardInterrupt:
  104. self.flowRunning = False
  105. def on_flow_cmd(api, data=None):
  106. OnionrFlow().start()
  107. def on_flowsend_cmd(api, data=None):
  108. err_msg = "Second arg is board name, third is quoted message"
  109. try:
  110. sys.argv[2]
  111. except IndexError:
  112. logger.error(err_msg, terminal=True)
  113. try:
  114. sys.argv[3]
  115. except IndexError:
  116. logger.error(err_msg, terminal=True)
  117. bl = onionrblocks.insert(sys.argv[3], header='brd',
  118. expire=(EXPIRE_TIME + epoch.get_epoch()),
  119. meta={'ch': sys.argv[2]})
  120. print(bl)
  121. def on_softreset(api, data=None):
  122. try:
  123. os.remove(identifyhome.identify_home() + '/board-index.cache.json')
  124. logger.info('Cleared Circles board cache')
  125. except FileNotFoundError:
  126. pass
  127. def on_processblocks(api, data=None):
  128. metadata = data['block'].bmetadata # Get the block metadata
  129. if data['type'] != 'brd':
  130. return
  131. b_hash = reconstructhash.deconstruct_hash(
  132. data['block'].hash) # Get the 0-truncated block hash
  133. board_cache = simplekv.DeadSimpleKV(identifyhome.identify_home(
  134. ) + '/board-index.cache.json', flush_on_exit=False) # get the board index cache
  135. board_cache.refresh()
  136. # Validate the channel name is sane for caching
  137. try:
  138. ch = metadata['ch']
  139. except KeyError:
  140. ch = 'global'
  141. ch_len = len(ch)
  142. if ch_len == 0:
  143. ch = 'global'
  144. elif ch_len > 12:
  145. return
  146. existing_posts = board_cache.get(ch)
  147. if existing_posts is None:
  148. existing_posts = []
  149. existing_posts.append(data['block'].hash)
  150. board_cache.put(ch, existing_posts)
  151. board_cache.flush()