Rain and Window Open Automation

You should avoid using {{ states | .....}} as it reevaluates the template on every entity update. At a minimum use {{ states.binary_sensor | ...}} but expand('group.contact_sensor_windows') is the better way to go.

- id: "b8064a7b-6c95-4ec2-a665-c26a0a830cad"
  alias: "Rain Alert Window Open"
  trigger:
    - platform: numeric_state
      entity_id: sensor.5123_weather_event_rain
      above: "0"
  action:
    - service: notify.telegram
      data:
        message: >
          It is raining and 
          {% set ow = expand('group.contact_sensor_windows')
          | selectattr('state', 'eq', 'on') | map(attribute='name') | list%}
          {%- set qty = ow | count -%}
          {%- if qty != 0 -%}
          {{-' the '}}{{-' and '.join((ow|join(', ')).rsplit(', ', 1)) }}
          {{-' is ' if qty==1 else ' are '}}open. 
          {% else %} all windows are closed. {% endif %}
        title: Rain Alert Window Open
  mode: single

FWIW, I preferred to have the areas for my notification instead of a long list of window entites.

Area-based template
{% set ow = expand('group.all_window_sensors')
|selectattr('state','eq','on') | map(attribute='entity_id') |
map('area_name')  | unique | reject('eq',None) | sort | map('lower') | list %}
{% set qty = ow | count %}
{% if qty != 0 %}
There {{ 'is an' if qty==1 else 'are'}} open window{{' ' if qty==1 else 's '}}in the 
{{' and '.join((ow|join(', ')).rsplit(', ', 1)) }}. 
{% else %} All windows are closed. {% endif %}