Граббер всех файлов телеграм канала

В свете того, что телеграм начали замедлять, появились опасения, что потеряю в конце концов доступ к телеграм каналу, где собрано много всяких интересных файлов. В связи с чем было принято решение спарсить и скачать всё его содержимое. Написанный скрипт проходит по каждому сообщению канала и скачивает все файлы в папки соответствующие идентифактору сообщения (или идентификатору группы сообщений). В общем получилось что-то вроде:

#!/usr/bin/env python3
# encoding: utf-8
import sys
import os,fcntl
import funcs
from telethon import TelegramClient
import requests
import configparser

## Защита от дубля запуска скрипта
fp = open(os.path.realpath(__file__), 'r')
try:
    fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
    sys.exit(0)

config = configparser.ConfigParser()
config.read("settings.ini")

print(config["Telegram"]["telegram_api_id"])

async def SaveFile(dir_name,message):
     if os.path.isdir(os.path.dirname(os.path.abspath(__file__))+f"/files/{dir_name}")==False:
       os.mkdir(os.path.dirname(os.path.abspath(__file__))+f"/files/{dir_name}")
     with open(os.path.dirname(os.path.abspath(__file__))+f"/files/{dir_name}/message.txt", 'w') as file:
        file.write(message.message)
     print(f"Вложения: {message.media}")
     if hasattr(message.media,"document"):
       if message.media.document != False:
         file_name=message.media.document.attributes[0].file_name
         print(f"-вложенный файл {file_name}")
         print("--сохраняю..")
         if os.path.isfile(os.path.dirname(os.path.abspath(__file__))+f"/files/{dir_name}/{file_name}")==False:
           await client.download_media(message.media, file=os.path.dirname(os.path.abspath(__file__))+f"/files/{dir_name}/{file_name}")
           print("--ок..")
         else:
           print("--уже скачивали!")
     if hasattr(message.media,"video"):
       if message.media.video!=False:
          print(f"-вложенное видео")

async def tele_news_parse():
    dp = await client.get_entity(config["Telegram"]["stl_chanel"])
    poz=int(config["Telegram"]["current_message_id"])
    print(f"-start at {poz}")
    async for message in client.iter_messages(dp,limit=2,offset_id=poz):
        print("---------------------------------------------------------")
        if hasattr(message, "message"):
            if message.message!=None:
                print(f"Сообщение: {message}")
                if message.grouped_id:
                   print("-its album!")
                   target_group_id = message.grouped_id
                   search_ids = range(message.id - 20, message.id + 21)
                   posts = await client.get_messages(config["Telegram"]["stl_chanel"], ids=list(search_ids))
                   media_group = []
                   for post in posts:
                     if post is not None and post.grouped_id == target_group_id and post.media is not None:
                      media_group.append(post)
                   for message in media_group:
                      print(f"--из альбома: {message}")
                      await SaveFile(target_group_id,message)
                else:
                  print("-its one file!")
                  await SaveFile(message.id,message)


    with open('settings.ini', 'w') as configfile:
        poz+=1
        print(f"-new poz at {poz}")
        config["Telegram"]["current_message_id"]=str(poz)
        config.write(configfile)

if __name__ == '__main__':
    client = TelegramClient("parser_data", config["Telegram"]["telegram_api_id"], config["Telegram"]["telegram_api_hash"])
    client.start()
    client.loop.run_until_complete(tele_news_parse())

Датчик давления BMP-280 на raspberry pi

Может работать как по I2C так и по протоколу SPI. В моём случае, я подключил по I2C.

Схема подключения:

BMP280 PinRaspberry Pi Pin
VCC3.3V (Pin 1)
GNDGround (Pin 6)
SDASDA (Pin 3, GPIO2)
SCLSCL (Pin 5, GPIO3)

Далее необходимо зайти в raspi-config и включить интерфейс i2c. После чего понадобится малинку перазагрузить. После перезагрузки можно посмотреть, определяется ли она:

i2cdetect -y 1

Должно появиться что-то вроде:

Иногда садится на 77 порт, но чаще на 76. Далее необходимо установить библиотеки:

sudo apt install -y python3-smbus python3-spidev python3-pip
pip3 install adafruit-circuitpython-bmp280

Минимальный код для проверки работоспособности:

#!/usr/bin/env python3
import board
import busio
import adafruit_bmp280
import time
import datetime
import requests
import json
import threading

# Create sensor object using the I2C port
i2c = busio.I2C(board.SCL,board.SDA)
sensor = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address = 0x76)


while True:
    print("\nTemperature: %0.1f C" % sensor.temperature)
    print("Pressure: %0.1f hPa" % sensor.pressure)
    mmHg = sensor.pressure * 0.75006
    print("Pressure: %0.1f mmHg" % mmHg)
    print("Altitude: %0.2f meters" % sensor.altitude)
    print("Sea Level Pressure: %0.1f hPa " % sensor.sea_level_pressure)
    print(datetime.datetime.now())
    time.sleep(10)

У меня выводит что-то подобное:

1с: Формулы в макете

Задача: при формировании файла формата xlsx из заполненного макета, необходимо вставить формулы.

Решение: в макете формулы вставить не возможно, из-за ограничений платформы 1С. Однако есть другой пусть — вставить формулы уже после формирования файла xlsx. Файл этого формата, по сути zip архив, в котором содержаться файлы xml с данными листов. Поэтому вполне возможно, проставив в макете в нужные формулы в текстовом формате, заменить их на «настоящие» уже в самом файле

Примечание: Задача решена на основе разработок описанных тут и тут. К себе на сайт утащил наработки, потому что-то с «интернетом творится что-то странное» — сегодня статьи есть, а завтра их уже снесли или разместили в платный раздел.

Всю работу по преобразованию разместил в отдельны модуль:


// Процедура - Извлечь архив, распаковывает ZIP-архив в указанный каталог
Процедура ИзвлечьАрхив(ПутьФайла, ПутьКаталога) Экспорт
   ДДФайла = Новый ДвоичныеДанные(ПутьФайла);
   Поток = Новый ПотокВПамяти(ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДДФайла));
   Попытка
       Архив = Новый ЧтениеZipФайла(Поток);
       Архив.ИзвлечьВсе(ПутьКаталога, РежимВосстановленияПутейФайловZIP.Восстанавливать);
       Архив.Закрыть();
   Исключение
       Сообщить("Ошибка при извлечении архива: " + ПутьФайла);
   КонецПопытки;
   Поток.Закрыть();                                                                                              
КонецПроцедуры

// Процедура - Собрать архив, упаковывает файлы из каталога в ZIP-архив
Процедура СобратьАрхив(ПутьКаталога, ПутьФайла) Экспорт
   Архив = Новый ЗаписьZipФайла(ПутьФайла);
   Архив.Добавить(ПутьКаталога + "\*.*",
   РежимСохраненияПутейZIP.СохранятьОтносительныеПути,
   РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно);
   Архив.Записать();
КонецПроцедуры

// Функция - Получить DOM-документ из XML-файла, возвращает XML-файл как DOM-модель
Функция ПолучитьDOMДокументИзXMLФайла(ПутьXMLФайла) Экспорт
   ЧтениеXML = Новый ЧтениеXML;
   ЧтениеXML.ОткрытьФайл(ПутьXMLФайла);
   ПостроительDOM = Новый ПостроительDOM;
   ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
   ЧтениеXML.Закрыть();
   Возврат ДокументDOM;
КонецФункции

// Процедура - Сохранить DOM-документ как XML-файл, записывает DOM в указанный файл
Процедура СохранитьDOMДокументКАКXMLФайл(ДокументDOM, ПутьXMLФайла) Экспорт
   ЗаписьXML = Новый ЗаписьXML();
   ЗаписьXML.ОткрытьФайл(ПутьXMLФайла);
   ЗаписьDOM = Новый ЗаписьDOM();
   ЗаписьDOM.Записать(ДокументDOM, ЗаписьXML);
   ЗаписьXML.Закрыть();
КонецПроцедуры

// Точка входа - непосредственно на входе - имя файла, где необходимо вставить
// формулы
Процедура ЗаполнитьФормулыВФайлеXLSX(ПутьДоФайла) экспорт    
    ВременныйКаталог = КаталогВременныхФайлов() + "\excel_temp\";    
    СоздатьКаталог(ВременныйКаталог);    
    ИзвлечьАрхив(ПутьДоФайла, ВременныйКаталог);    
    ПутьДоXMLФайлаЛист1 = ВременныйКаталог + "xl\worksheets\sheet1.xml";
    // Чтение распакованного xml-файла листа 1 в ДокументDOM
    ДокументDOM = ПолучитьDOMДокументИзXMLФайла(ПутьДоXMLФайлаЛист1);
    СтроковыеЗначения = ПолучитьDOMДокументИзXMLФайла(ВременныйКаталог + "xl\sharedStrings.xml");    
    ВсеЯчейки = ДокументDOM.ПолучитьЭлементыПоИмени("c");
    ВсеСтроки = ДокументDOM.ПолучитьЭлементыПоИмени("row");
    ВсегоСтрок = ВсеСтроки.Количество();
    ВсеСтроковыеЗначения = СтроковыеЗначения.ЭлементДокумента.ДочерниеУзлы;    
    // Обход ячеек и определение формул по произвольному алгоритму
    Для Каждого Ячейка Из ВсеЯчейки Цикл        
        ИмяЯчейки = Ячейка.ПолучитьАтрибут("r");
        Если ИмяЯчейки = Неопределено Тогда Продолжить;КонецЕсли;        
        ИмяСтолбца = Сред(ИмяЯчейки, 1, 1);
        ИмяСтроки = Сред(ИмяЯчейки, 2);        
        НомерСтроки = Число(ИмяСтроки);    
        ТипЯчейки = Ячейка.ПолучитьАтрибут("t");
        Если ТипЯчейки="s" Тогда            
           Если Ячейка.ПервыйДочерний.ИмяУзла = "v" Тогда // здесь лежит какое-то значение  
           // надо найти значение в строках SharedStrings    
              ЗначениеЯчейки = ВсеСтроковыеЗначения[Число(Ячейка.ПервыйДочерний.ТекстовоеСодержимое)];                    
                    ЭтоФормула=СтрНачинаетсяС(ЗначениеЯчейки.ТекстовоеСодержимое,"="); //    Формула начинается с "="
                    Если ЭтоФормула Тогда 
                        формула=ЗначениеЯчейки.ТекстовоеСодержимое;
						формула=СтрЗаменить(формула,"=","");
                        Пока Ячейка.ЕстьДочерниеУзлы() Цикл
                            Ячейка.УдалитьДочерний(Ячейка.ПервыйДочерний);
                        КонецЦикла;                        
                        // Заполнение формулы
                        ЭлФормула = ДокументDOM.СоздатьЭлемент("f");
                        ЭлФормула.ТекстовоеСодержимое = Формула;                    
                        Ячейка.ДобавитьДочерний(ЭлФормула);
                    КонецЕсли;
           КонецЕсли;
        КонецЕсли;
    КонецЦикла;    
    // Запись модифицированного объекта ДокументDOM в xml-файл листа 1
    СохранитьDOMДокументКАКXMLФайл(ДокументDOM, ПутьДоXMLФайлаЛист1);    
    УдалитьФайлы(ПутьДоФайла);    
    СобратьАрхив(ВременныйКаталог, ПутьДоФайла);    
    УдалитьФайлы(ВременныйКаталог);    
КонецПроцедуры

Как использовать:

ТабДок=Новый ТабличныйДокумент;
Макет=ПолучитьОбщийМакет("ШаблонЛКУКЛС");
...
ОбластьТушка=Макет.ПолучитьОбласть("Тушка");
ОбластьТушка.Параметры.Разница="=G"+(ГСч+1)+"-H"+(ГСч+1); // = означает что тут формула в ячейке
ТабДок.Вывести(ОбластьТушка);					
...

ВрФайл=ПолучитьИмяВременногоФайла("xlsx");
ТабДок.Записать(ВрФайл,ТипФайлаТабличногоДокумента.XLSX);
						
СК_ГР_ФормулыВмакете.ЗаполнитьФормулыВФайлеXLSX(ВрФайл); // А теперь магия! Вставим формулу..

1С: Нечитаемые символы в табличном документе

А вот нежданно и негаданно, оказывается что в табличный документ нельзя помещать некоторые нечитаемые символы. Если это произошло, то 1С при стандартном табдок.Показать();, тоже покажет…ошибку:

«Текст XML содержит недопустимый символ в позиции 1 :»

Пришлось внедрять функцию, которая чистит строку от «мусора», и фильтровать размещение данных в параметры макета через неё:

Функция УдалитьНечитаемыеСимволы(ВхСтрока) Экспорт
   // Удаляем переносы строк
   ИсхСтрока = СтрЗаменить(ВхСтрока,  Символы.ПС,   " "); // Перевод строки
   ИсхСтрока = СтрЗаменить(ИсхСтрока, Символы.ВК,   " "); // Возврат каретки
   ИсхСтрока = СтрЗаменить(ИсхСтрока, Символы.НПП,  " "); // Неразрывный пробел \xA0
   ИсхСтрока = СтрЗаменить(ИсхСтрока, Символы.ПФ,   " "); // Перевод формы (страницы).
   ИсхСтрока = СтрЗаменить(ИсхСтрока, Символы.ВТаб, " "); // Символ табуляции (вертикальной)
   ИсхСтрока = СтрЗаменить(ИсхСтрока, Символы.Таб,  " "); // Символ табуляции (горизонтальной).
   ИсхСтрока = СтрЗаменить(ИсхСтрока, Символ(182),  " "); // Символ параграфа
   ИсхСтрока = СтрЗаменить(ИсхСтрока, Символ(176),  " "); // Символ градуса
   ИсхСтрока = СтрЗаменить(ИсхСтрока, Символ(160),  " "); // Неразрывный пробел симв.

   // Удаляем маркеры списков
   ИсхСтрока = СтрЗаменить(ИсхСтрока, "•", "-"); // маркер 1
   ИсхСтрока = СтрЗаменить(ИсхСтрока, "◦", "-"); // маркер 2

   // Удаляем спец.HTML и Word
   ИсхСтрока = СтрЗаменить(ИсхСтрока," ", " "); //  HTML-пробел
   ИсхСтрока = СтрЗаменить(ИсхСтрока,"&",  " ");  // Раскодировать (& → &).
   //ИсхСтрока = СтрЗаменить(ИсхСтрока, "&lt;", " "); // экранированное представление символа <
   //ИсхСтрока = СтрЗаменить(ИсхСтрока, "&gt;", " "); // экранированное представление символа >
   ИсхСтрока = СтрЗаменить(ИсхСтрока, "¶", " "); // абзац симв.
   ИсхСтрока = СтрЗаменить(ИсхСтрока, "�",  ""); // битый символ

   // Удаляем все управляющие символы (ASCII 0–31), \x01и \x0B
   Для i = 0 По 31 Цикл
       ИсхСтрока = СтрЗаменить(ИсхСтрока, Символ(i), "");
   КонецЦикла;

   // Удаляем лишние пробелы по краям
   ИсхСтрока = СокрЛП(ИсхСтрока);

   // Удаляем двойные пробелы внутри строки
   Пока Найти(ИсхСтрока, "  ") > 0 Цикл
       ИсхСтрока = СтрЗаменить(ИсхСтрока, "  ", " ");
   КонецЦикла;
   Возврат ИсхСтрока;  //Возвращает очищенную строку (тип: Строка)
КонецФункции

1С: Аутентификационный контекст клиента отсутствует в рабочем процессе

Данная ошибка возникла у меня, когда я в фоновом задании попытался вставить данные во внешнюю базу данных. Хотя если просто на сервере выполнять кусок когда вида:

	зап=внешниеисточникиданных.ОтчетыДля1С.Таблицы.reports_1c_public_raw_ias.СоздатьОбъект();	
	зап.communal="1"
    зап.Записать();	

То всё корректно отрабатывалось. Решением оказалось: перед любой операцией со внешним источником данных в фоновом задании проверять установлено ли соединение, и если нет — предварительно его устанавливать:

	состояние=ВнешниеИсточникиДанных.ОтчетыДля1С.ПолучитьСостояние();
	если состояние=СостояниеВнешнегоИсточникаДанных.Отключен тогда
		УстановитьПривилегированныйРежим(Истина);		
		Соединение = Новый ПараметрыСоединенияВнешнегоИсточникаДанных;    
		Соединение.АутентификацияОС=ложь;
		Соединение.АутентификацияСтандартная=ложь;
		Соединение.ИмяПользователя="укацукацу";
		Соединение.Пароль="цукаукацук";
		Соединение.СтрокаСоединения="DRIVER={PostgreSQL Unicode}; Data Source=PostgreSQL35W; SERVER=цукацука; PORT=5432; DATABASE=reports_1c; UID=цукацукацу; PWD=укацукацук";
		Соединение.СУБД="PostgreSQL";
		
		ВнешнийИсточник=ВнешниеИсточникиДанных.ОтчетыДля1С;
		ВнешнийИсточник.УстановитьОбщиеПараметрыСоединения(Соединение);
		ВнешнийИсточник.УстановитьПараметрыСоединенияПользователя(ПользователиИнформационнойБазы.ТекущийПользователь(),Соединение);	
		ВнешнийИсточник.УстановитьОбщиеПараметрыСоединения(Соединение);
		ВнешнийИсточник.УстановитьПараметрыСоединенияСеанса(Соединение);
		ВнешнийИсточник.УстановитьСоединение();
	
	конецесли;
1 2 3 312