powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Идеальная рассылка почты из PL/SQL UTL_SMTP
14 сообщений из 14, страница 1 из 1
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926238
Братцы, да понятно, что все знают UTL_MAIL, UTL_SMTP и даже где-то UTL_TCP. У меня тоже код работает по рассылке почты от корпоративного почтаря. Рассылается статус заказов пользователям облачного приложения ну и всякое такое деловое, неспамовское по сути изначально. Всё было хорошо, пока не тукнуло в mail.ru спам-блокировкой почты домена. Юноши там немного странные, но речь не о них. В итоге я даже немного благодарен этому не совсем адекватному с моей скромной точки зрения почтарю.

Итак, поскольку во мне живёт программер, раззудилось добиться от четырёх источников gmail, ya.ru, mail.ru и mail-tester.com сердечного одобрения. От роботов конечно.

В итоге получил по текстовым и письмам с одним вложением (pdf, docx) X-Spam-Status: score=-2.9 по первым трём и чистые 10/10 для последнего.
Привожу код в надежде, что здесь полно умных людей и они толково ещё что-то скажут. Хотя я не уверен, что результат можно улучшить ))

Прицепом замечу, что APEX_MAIL так себе пакетик. А за utl_encode и base64_encode нужен глаз да глаз: то недолив переноса строк, то перелив.

1. Пока у вас нет dkim, spf и dmarc на домене, можете не мечтать особо.
2. Если есть, то код получился примерно таким.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
CREATE OR REPLACE PROCEDURE SEND_MAIL (
                                       p_to        IN VARCHAR2,
                                       p_from      IN VARCHAR2,
                                       p_from_name IN VARCHAR2,
                                       p_reply_to  IN VARCHAR2,
                                       p_subject   IN VARCHAR2,
                                       p_body      IN VARCHAR2 DEFAULT NULL,
                                       p_body_html IN VARCHAR2 DEFAULT NULL,
                                       p_attach_name IN VARCHAR2 DEFAULT NULL,
                                       p_attach_mime IN VARCHAR2 DEFAULT NULL,
                                       p_attach_blob IN BLOB DEFAULT NULL)
AS
  l_mail_conn   UTL_SMTP.connection;
  l_boundary1   varchar2(50);
  l_boundary2   varchar2(50);
  l_step        pls_integer  :=9000;
  l_smtp_host   varchar2(50) := 'Ваш почтарь';
  l_smtp_port   number := 25;
  l_attach_mime varchar2(255); 

function encodeQ(subj_str varchar2) return varchar2
AS
begin
  return replace(UTL_ENCODE.MIMEHEADER_ENCODE (subj_str, 'utf8', UTL_ENCODE.QUOTED_PRINTABLE),UTL_TCP.crlf,''); -- replace важно!
end;

function get_boundary return varchar2 is
begin
 return upper(MD5HASH(current_timestamp));
end;

BEGIN
  l_boundary1 := '--=:'||get_boundary||':';
  l_boundary2 := '--=:'||get_boundary||':';
  
  l_mail_conn := UTL_SMTP.open_connection(l_smtp_host, l_smtp_port);
  UTL_SMTP.helo(l_mail_conn, l_smtp_host);
  UTL_SMTP.mail(l_mail_conn, p_from);
  UTL_SMTP.rcpt(l_mail_conn, p_to);
  UTL_SMTP.open_data(l_mail_conn);
  
  UTL_SMTP.write_data(l_mail_conn, 'MIME-Version: 1.0' || UTL_TCP.crlf);  
  UTL_SMTP.write_data(l_mail_conn, 'To: ' || p_to || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'From: ' || encodeQ(p_from_name) || '<' || p_from ||'>' || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Reply-To: ' || nvl(p_reply_to, p_from) || UTL_TCP.crlf);
  if p_attach_name is null then
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: multipart/alternative; boundary="' || l_boundary1 || '"' || UTL_TCP.crlf);
  else
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: multipart/mixed; boundary="' || l_boundary1 || '"' || UTL_TCP.crlf);
  end if;
  UTL_SMTP.write_data(l_mail_conn, 'Content-Transfer-Encoding: 8bit'|| UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Date: ' || to_char(current_timestamp,'Dy, DD Mon YYYY HH24:MI:SS TZHTZM', 'NLS_DATE_LANGUAGE=AMERICAN') || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Subject: ' || encodeQ(nvl(p_subject,'Тема письма не указана.')) || UTL_TCP.crlf);

  -- для почтарей комментируем этот кусок, а для mail-tester оставляем
  UTL_SMTP.write_data(l_mail_conn, 'List-Unsubscribe: <mailto:unsubscribe@вашдомен.com?subject=Unsubscribe>'|| UTL_TCP.crlf ); 
  UTL_SMTP.write_data(l_mail_conn, 'List-Subscribe: <mailto:subscribe@вашдомен.com?subject=Subscribe>'|| UTL_TCP.crlf );
  --------------------------------------------------------------------
  
  UTL_SMTP.write_data(l_mail_conn, 'List-Owner: <mailto:abuse@вашдомен.com?subject=Abuse>'|| UTL_TCP.crlf );
  UTL_SMTP.write_data(l_mail_conn, 'List-Help: <mailto:help@вашдомен.com?subject=Help>'|| UTL_TCP.crlf || UTL_TCP.crlf);
  
  UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary1 || UTL_TCP.crlf);
  UTL_SMTP.write_data(l_mail_conn, 'Content-Type: multipart/alternative; boundary="' || l_boundary2 || '"' || UTL_TCP.crlf || UTL_TCP.crlf);
   
  if p_body is not null then
    UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary2 || UTL_TCP.crlf);
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: text/plain; charset="utf-8"' || UTL_TCP.crlf || UTL_TCP.crlf);
    UTL_SMTP.write_raw_data(l_mail_conn, UTL_RAW.cast_to_raw(p_body)); -- write_raw_data важно
    UTL_SMTP.write_data(l_mail_conn, UTL_TCP.crlf);
  end if;

  if p_body_html is not null then
    UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary2 || UTL_TCP.crlf);
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: text/html; charset="utf-8"' || UTL_TCP.crlf || UTL_TCP.crlf);
    UTL_SMTP.write_raw_data(l_mail_conn, UTL_RAW.cast_to_raw(p_body_html)); -- write_raw_data важно
    UTL_SMTP.write_data(l_mail_conn, UTL_TCP.crlf );
  end if;
  
  UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary2 || '--' || UTL_TCP.crlf || UTL_TCP.crlf);
  
  if p_attach_name is not null then
    UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary1 || UTL_TCP.crlf);
    
    if instr(p_attach_name,'.pdf')>0 then l_attach_mime := 'application/pdf';  
    elsif instr(p_attach_name,'.docx')>0 then l_attach_mime := 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
    else l_attach_mime := p_attach_mime;
    end if;
    
    UTL_SMTP.write_data(l_mail_conn, 'Content-Type: ' || l_attach_mime || '; name="' || p_attach_name || '"' || UTL_TCP.crlf);
    UTL_SMTP.write_data(l_mail_conn, 'Content-Disposition: attachment; filename="' || p_attach_name || '"' || UTL_TCP.crlf);
    UTL_SMTP.write_data(l_mail_conn, 'Content-Transfer-Encoding: base64' || UTL_TCP.crlf || UTL_TCP.crlf);

    for i in 0 .. trunc((DBMS_LOB.getlength(p_attach_blob) - 1 )/l_step) 
    loop
      UTL_SMTP.write_data(l_mail_conn, UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(DBMS_LOB.substr(p_attach_blob, l_step, i * l_step + 1))) || UTL_TCP.crlf); -- || UTL_TCP.crlf важно 
    end loop; 

    UTL_SMTP.write_data(l_mail_conn, UTL_TCP.crlf || UTL_TCP.crlf);
  end if;

  UTL_SMTP.write_data(l_mail_conn, '--' || l_boundary1 || '--' || UTL_TCP.crlf);
  UTL_SMTP.close_data(l_mail_conn);

  UTL_SMTP.quit(l_mail_conn);
  
  exception 
    when others then
            UTL_SMTP.close_data(l_mail_conn);
            UTL_SMTP.quit(l_mail_conn);          
END;



PS Что касается mail.ru, то юноши эти могут запросто отреагировать на пару тычков в спам некоего вредного (или злонамеренного) пользователя и занести весь ваш домен в спам-лист. Также опция "Отписаться" напрочь игнорирует у них RFC, а просто направляет вашего отправителя в спам.
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926243
А да, виноват, вот код хэша, не помню, откуда взял, но работает )))
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
CREATE OR REPLACE FUNCTION MD5HASH (str IN VARCHAR2)
	RETURN VARCHAR2
	IS v_checksum VARCHAR2(32);
 
	BEGIN
		if str is null then 
      v_checksum:='';
    else 
      v_checksum := lower( rawtohex( utl_raw.cast_to_raw( sys.dbms_obfuscation_toolkit.md5(input_string => str) ) ) );
    end if;
    
		RETURN v_checksum;
		EXCEPTION
			WHEN NO_DATA_FOUND THEN
      return '';
			NULL;
      
		WHEN OTHERS THEN
			-- Consider logging the error and then re-raise
      return '';
			RAISE;
	END md5hash;
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926251
Фотография -2-
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Саша Сплинтер
что-то скажут
Слишком много говна, чтобы раскладывать его по полочкам.
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926252
-2-,

Давайте свой код сюда, который решает поставленную задачу. У меня никаких претензий.
С удовольствием буду пользоваться.
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926260
123йй
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Саша Сплинтер,

здесь, на форуме, был более интересный пакет
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926265
Да здесь на форуме вообще полно всякого. Но я не нашёл.
Просто мой код задачу решает какую я отписал. А сферически рассылать умеет полинтернета.
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926268
123йй
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Саша Сплинтер,

учись .
Тогда свою подделку не будешь называть "Идеальная рассылка"
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926282
Вообще супер!
И чего там только нету! В этой тонне кода!
Пару слов в защиту моей поделки (одна "д").

Моя поделка маленькая и решает, как я написал, вполне конкретную задачу 10 из 10. что я и назвал идеальной рассылкой.

У автора в его шикарном пакете задачи другие. И если прогнать по описанным критериям рассылку с помощью его пакета, то результаты хорошие, но не отличные.

Так что пишите ещё )
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926288
Добавлю, что с этого у меня все и начиналось, когда вопрос встал не о мега-пакете (да мне такого и не написать), а о результатах тестов текста сообщения:


-0.001 BAD_ENC_HEADER Message has bad MIME encoding in the header
-2.019 BASE64_LENGTH_79_INF base64 encoded email part uses line length greater than 79 characters
-1.105 MIME_HTML_ONLY Message only has text/html MIME parts

Так что всё честно я написал про идеальную рассылку. Ну а код идеальный или нет, так это вы бизнесу, который оплачивает ваши расходы, расскажите, что есть идеальный код, а что есть решающий задачу код.

За ссылку на пакет благодарю.
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926290
Фотография Elic
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Саша Сплинтер
расскажите, что есть идеальный код, а что есть решающий задачу код.
Чего не навыдумывают, чтобы хвастаться свои говнокодом.
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926293
Вот ещё один достойный комментарий!
Итак, а вы что предложите, чей чужой пакет проверить?
Ну или может снизойдёте и укажете на причины Вашей столь нелестной оценки?
В самом деле буду благодарен.
Кушать не смогу теперь! Вам до 30 000 сообщений всего ничего осталось! Дерзайте!
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926295
автор-2.019 BASE64_LENGTH_79_INF base64 encoded email part uses line length greater than 79 characters

Вот эта ерунда получается вот здесь

UTL_SMTP.write_data(l_mail_conn, UTL_RAW.cast_to_varchar2(UTL_ENCODE.base64_encode(DBMS_LOB.substr(p_attach_blob, l_step, i * l_step + 1))) || UTL_TCP.crlf);

Если не добавлять || UTL_TCP.crlf к UTL_RAW.cast_to_varchar2, то, поскольку UTL_ENCODE.base64_encode не оканчивает блок crlf, происходит объединение двух строк в одну.

В мегапакете, автор которого в этой дискуссии отсутствует, этот кусок выглядит так

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
   LOOP
      BEGIN
        dbms_lob.read(message, v_amt, ps, vBuf);
        ps := ps + v_amt;
        utl_smtp.write_raw_data (v_Mail_Conn,utl_encode.base64_encode ( utl_raw.cast_to_raw(convert(vBuf,'UTF8'))));
      EXCEPTION
        WHEN no_data_found THEN
            EXIT;
      END;
    END LOOP;
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926404
merch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Саша СплинтерПросто мой код задачу решает какую я отписал
Саша СплинтерМоя поделка маленькая и решает, как я написал, вполне конкретную задачу 10 из 10
Саша СплинтерТак что всё честно я написал про идеальную рассылку

а что Вы от форумчан хотите? Восхищения? Или чтобы мы сели и написали код, решающий вашу задачу на 11 из 10?
...
Рейтинг: 0 / 0
Идеальная рассылка почты из PL/SQL UTL_SMTP
    #39926436
SQL*Plus
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
merch
Саша СплинтерПросто мой код задачу решает какую я отписал

Саша СплинтерМоя поделка маленькая и решает, как я написал, вполне конкретную задачу 10 из 10
Саша СплинтерТак что всё честно я написал про идеальную рассылку

а что Вы от форумчан хотите? Восхищения? Или чтобы мы сели и написали код, решающий вашу задачу на 11 из 10?
Саша Сплинтер!

Несите сюда пятилитровую бутылку хорошего 12 летнего виски!
Будем обмывать успех.

Никто не возражает?

P.S. splinter = щепка, осколок, заноза
:-)
...
Рейтинг: 0 / 0
14 сообщений из 14, страница 1 из 1
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Идеальная рассылка почты из PL/SQL UTL_SMTP
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]